🌰: Однажды директор сообщает вам, что на странице компании нужно отображать время просмотра, начиная с момента открытия страницы и добавляя по единице каждую секунду.
Хорошо, получайте задание и начинайте анализировать вопросы
Суть этого дела в том, чтобы реализовать аккумулятор, каждую секунду проходящийавтоматическое приращениеоднажды. Хорошо, приступим к реализации последовательно.
Сначала самое простое»увеличивать каждую секунду", мы сразу подумали о созданиитаймер, вызывается каждую 1 секундуаккумулятор, предполагая, что функция аккумулятора вызываетсяcounterЗапишите код с уверенностью.
setInterval(function () {
// 调用累加器
counter();
}, 300);
Мы уже написали логику вызова аккумулятора, теперь, поскольку нам нужно отобразить количество секунд, мы должны определить переменную для хранения количества секунд. И автоинкрементировать его в аккумуляторе. Продолжить с полным кодом.
// 秒数
let second = 0;
// 累加器
function counter() {
second += 1;
}
setTimeInterval(function () {
// 调用累加器
counter();
}, 300);
Мы уже написали ключевой код для решения дела, потому что цель этой статьи — помочь вам понять замыкания, поэтому количество секунд (second) отображаемый на странице код писать не будем. Чтобы убедиться, что код правильный, мы можем напечатать его в концеsecondЗначение , и чтобы не тратить место, мы добавляем временное условие при тестированииОстановить накопление через 10 секунд, Изменить наш код
// 秒数
let second = 0;
// 累加器
function counter() {
second += 1;
return second;
}
const recordSecond = setInterval(function () {
// 到达10秒后停止
if (second === 10) {
clearInterval(recordSecond);
console.log('计时结束!');
return;
}
// 调用累加器,输出当前秒数
console.log(`${counter()}秒`);
}, 1000);
Давайте проверим код на консоли, и результаты следующие:
Видно, что мы реализовали необходимые функции, что также является наиболеенизкий уровеньметод реализации.
почему самыйнизкий уровеньДа, потому что наша идея реализации функции состоит в том, чтобы определитьглобальная переменная, каждое накопление должно обновить этоглобальная переменная, и во всех языках программирования есть неписаный железный закон — определяйте как можно меньше глобальных переменных.
Есть примерно две причины, одну трудно контролировать, а другая занимает память.
1.глобальная переменнаяЕго нелегко контролировать, и его можно читать и записывать где угодно, а это означает, что он может быть перезаписан посторонними программами.
2.глобальная переменнаяЖизненный цикл занимаемой памяти, как правило, длинный.локальная переменная(переменная, определенная в функции), после завершения вызова функции соответствующая среда выполнения будет вытолкнута из стека выполнения, а механизм рециркуляции будет время от времени выполнять операцию рециркуляции, чтобы освободить память, которая не используется. нужно занять. Открытие среды выполнения говорит механизму повторного использования: «Мне больше не нужны эти переменные, их можно переработать». а такжеглобальная переменнаяПоскольку он может быть прочитан и записан в любом месте любой программой в любое время, механизму повторного использования трудно подсчитать, когда его нужно освободить.глобальная переменнаязанятой памяти, что приводит кглобальная переменнаяОбычно он выпускается, когда глобальная среда выполнения уничтожается.
После некоторых замечаний выше, что нам нужно сделать дальше, это одновременно реализовать функцию,Избегайте определения глобальных переменных для загрязнения окружающей среды.
Все, что нам нужно изменить после приведенного выше кода, — это определить глобальныйsecondизменить налокальная переменная, тем самым защищаяsecondНе влияет на другие операции. подумайте оsecondгде заявление...
потому что мы будемsecondОпределенная как локальная переменная, мы сначала оптимизируем код прямо сейчас, иsecondФункции чтения и записи записаны как можно дальше внутри функции для простого обслуживания.
Во-первых, время остановки определяется на задачах условияcounterв функции,
// 秒数
let second = 0;
// 累加器
function counter() {
// 到达10秒后停止
if (second === 10) {
clearInterval(recordSecond);
console.log('计时结束!');
return;
}
second += 1;
console.log(`${second}秒`);
}
const recordSecond = setInterval(function () {
// 调用累加器,输出当前秒数
counter();
}, 1000);
Давайте протестируем код на консоли, чтобы увидеть, работает ли он, и результаты следующие:
Хорошо, теперь наш код немного понятнее, чем в первой версии.В callback-функции временной задачи она отвечает только за вызов накопителя.При остановке накопления и выполнении накопления он передается наcounterконтролировать.
Далее продолжаем вышеописанные действия - будетsecondопределяется как локальная переменная, потому чтоsetIntervalФункция обратного вызова будет выполняться каждую секунду, еслиsecondОбъявлено в функции обратного вызова, каждый раз, когда вызывается функция обратного вызоваsecondбудет инициализирован, то накопительный эффект не будет достигнут.
Поэтому мы считаемsecondобъявлено вcounterВнутри функции код выглядит следующим образом
// 累加器
function counter() {
// 秒数
let second = 0;
// 到达10秒后停止
if (second === 10) {
clearInterval(recordSecond);
console.log('计时结束!');
return;
}
second += 1;
console.log(`${second}秒`);
}
const recordSecond = setInterval(function () {
// 调用累加器,输出当前秒数
counter();
}, 1000);
Вы, должно быть, нашли проблему, потому чтоcounterВызов находится в функции обратного вызова, тогда он будетsecondопределено вcounter, инициализированной операции нельзя избежать.
Подумайте о том, какcounterвнутренний,secondОперации, связанные с инициализацией и накоплением, разбиты на две части, по идее будемcounterизменил его
// 累加器
function counter() {
// 秒数
let second = 0;
function doCounter() {
// 到达10秒后停止
if (second === 10) {
clearInterval(recordSecond);
console.log('计时结束!');
return;
}
second += 1;
console.log(`${second}秒`);
}
}
const recordSecond = setInterval(function () {
// 调用累加器,输出当前秒数
counter();
}, 1000);
Как видите, из структуры кода мы явноcounterДелится на две части. потому чтоdoCounterдаcounterвнутренняя функция, поэтому имеет доступ к переменным в области внешней функции.
Но есть проблема с текущим кодом, мы просто организуем операции по накоплению в функцию, но не возвращаем эту функцию, так что даже еслиcounterвызывается и выполняет только свою внутреннюю переменнуюsecondи функцияdoCounterоперация декларации.
очень просто, мыdoCounterфункционировать какcounterвозвращаемое значение функции, затемcounterпри вызове эквивалентноdoCounterвызывается, измените код следующим образом
// 累加器
function counter() {
// 秒数
let second = 0;
function doCounter() {
// 到达10秒后停止
if (second === 10) {
clearInterval(recordSecond);
console.log('计时结束!');
return;
}
second += 1;
console.log(`${second}秒`);
}
return doCounter;
}
const recordSecond = setInterval(function () {
// 调用累加器,输出当前秒数
counter();
}, 1000);
Давайте скопируем код в консоль, чтобы проверить его.Эй, случилось что-то странное, и консоль не выводит никакой информации...
ДоказыватьdoCounterне было реализовано должным образом. Взгляните на код, чтобы найти, где проблема.
Давайте сосредоточимся наcounterфункциональныйreturnчасть,
// 累加器
function counter() {
// 秒数
let second = 0;
function doCounter() {
// 到达10秒后停止
if (second === 10) {
clearInterval(recordSecond);
console.log('计时结束!');
return;
}
second += 1;
console.log(`${second}秒`);
}
return doCounter;
}
Мы знаем, что функция обратного вызова временной задачи обязательно будет выполнена, когда условие уничтожения не будет выполнено, так что правильноcounterВызов функции также должен быть выполнен.
мы будемdoCounterфункционировать какcounterвозвращаемое значение функции, затемcounterВызов функции может получитьcounterВозвращаемое содержимое функции, проблема здесь!!
мы просто положилиdoCounterфункция возвращает,counterВызов функции просто возвращаетdoCounterфункция, ноdoCounterнигде не вызывается.
Для удобства чтения будем называтьcounterВозвращаемое содержимое функции может быть присвоено переменнойdoCounterFn,а такжеdoCounterFnЭто функция, которая фактически выполняет операции, связанные с накоплением, т. е.setIntervalО чем действительно должна заботиться функция обратного вызова. Продолжайте изменять код следующим образом
// 累加器
function counter() {
// 秒数
let second = 0;
function doCounter() {
// 到达10秒后停止
if (second === 10) {
clearInterval(recordSecond);
console.log('计时结束!');
return;
}
second += 1;
console.log(`${second}秒`);
}
return doCounter;
}
// 得到累加器
const doCounterFn = counter();
const recordSecond = setInterval(function () {
// 调用累加器
doCounterFn();
}, 1000);
Чтобы упомянуть концепцию, когда мы присваиваем возвращаемое значение вызова функции переменной, если вызванная функция возвращает функцию, то переменная получает то, чтофункция возвратаСсылка (адрес памяти возвращаемой функции), когда функция имеет ссылочное отношение, механизм сборки мусора не будет высвобождать занимаемую ею память, то есть будет сохранять память, занятую всем контекстом выполнения функции (переменная объект, цепочка областей действия и т. д.). И поскольку возвращаемая функция также является внутренней функцией функции, генерируется вложенность областей, образуя замыкание Цепочка областей действия возвращаемой функции включает область действия внешней функции, то есть к возвращаемой функции можно получить доступ Переменные, определенные в внешние функции.
Поэтому, когда мы проходимdoCounterFnнепрямой вызовdoCounterкогда, хотяdoCounterFnПеременная не существует на цепочке объемаsecond,ноdoCounterК нему все еще можно получить доступ при выполнениицепочка прицеловпеременная on , где она была объявленалюбая переменная в области видимости, что является классическим примером расширения области видимости.
пройти черезcounterа такжеdoCounterДве функции вложены для формирования вложенности областей. Вложенная функция должна получить доступ к области, в которой она расположена, а затем вызвать вложенную функцию в другой области. Весь этот процесс мы называем замыканием. Сумка.
Давайте запустим приведенный выше код для тестирования, и результаты будут следующими:
До сих пор мы выполнили требования относительно идеально.
Благодаря этому примеру у нас также есть понимание процесса формирования замыкания Наконец, мы суммируем замыкание:
1. Когда вам нужно использовать замыкания?
Когда нам нужно повторно использовать объект, но мы хотим защитить этот объект от загрязнения другим кодом.
2. Роль замыканий
Предоставляет внешней функции доступ к области внутренней функции.
3. Необходимые условия образования замыканий
- нужна область доступа
- Вложение функций (физические условия)
- Вложенная функция вызывается в другой внешней области
4. Недостатки замыканий
По сравнению с обычными закрытиями функций занимает больше памяти, поэтому рекомендуется вручную назначать пустые метки для повторного использования после использования.fn = null.
На этом эта статья закончена, и те, кто усердно будет ее читать, станут великими талантами!