javascript почти мифическая концепция: замыкания

JavaScript

написать впереди

JavaScript — почти миф Для людей, которые имеют опыт работы с JavaScript, но никогда по-настоящему не понимают концепцию замыканий, можно сказать, что понимание замыканий является своего рода перерождением. Замыкания не являются инструментами, для использования которых требуется изучение нового синтаксиса. Замыкания являются естественным следствием написания кода, основанного на лексической области видимости. Другими словами, вам не нужно писать замыкания ради замыканий, замыкания повсюду в коде, который мы пишем. Когда вы действительно разберетесь в замыканиях, вы обнаружите, ох~, как много замыканий появилось в коде, который я набрал раньше!

небольшая демонстрация

Если мы внимательно посмотрим на следующие примеры, мы почувствуем себя странно: очевидно, вызывается результат(), почему результаты отличаются?

let count=500 //全局作用域
function foo1() {
  let count = 0;//函数全局作用域
  function foo2() {
    count++;//函数内部作用域
    console.log(count);
    return count;
  }
  return foo2;//返回函数
}
let result = foo1();
result();//结果为1
result();//结果为2

Прежде всего, foo1() возвращает функцию foo2(). Когда мы вызываем result(), она возвращает функцию, выполняемую foo2(). Что находится в foo2()? Во-первых, мы видим, что есть переменная count, как показано ниже. , но определения нет. Согласно определению цепочки областей видимости JavaScript, когда переменная внутри функции не определена, она будет использовать метод всплытия для поиска верхнего уровня. Верхний уровень не следует за предыдущим уровнем до тех пор, пока окно верхнего уровня. Если его нет, будет сообщено о неопределенной ошибке. Здесь мы находим count в foo1(), поэтому count+1, первый вывод равен 1, проблем нет.

 function foo2() {
    count++;
    console.log(count);
    return count;
  }

Но есть проблема, когда мы выполняем результат() во второй раз.Почему он 2.Согласно процессу, сначала ищем count внутри функции foo2(), если нет, то идем во внешний слой, чтобы найти count= 0, это В это время count+1 должно быть 1. Вот проблема замыкания.

Сначала мы добавляем отладчик в исходный код, затем щелкаем правой кнопкой мыши в Google Chrome, чтобы проверить, щелкаем ресурсы, чтобы увидеть замыкание справа, и визуализация браузера подтверждает, что это действительно замыкание. был сохранен в замыкании, это означает, что count=1 не был уничтожен, а count=2 при вызове result() в следующий раз.

Признать масштаб

Чтобы изучить Clusure, вы должны понимать область применения JavaScript. Объем включает в себя:
1. Глобальный масштаб
2. Объем функций
4. Область действия на уровне блоков (новое в es6, решить проблему var, добавить let, const)

  var count = 100; //全局作用域
  function foo1() {
    var count = 0; //函数全局作用域
    return count; //返回函数
  }
  if (count == 1) {
    //块级作用域
    console.log(count);
  }

В приведенном выше коде легко увидеть классификацию области действия.Следует отметить, что функция (функция) также является областью блочного уровня.Короче говоря, обычно{}можно считать областью действия блока.

цепочка прицелов

Области вложены в области, образуя цепочку областей. Внешняя область не может получить доступ к внутренней области, см. следующий пример

function foo(){
var n=1
function foo2(){
  var m=1
  console.log(n) //1
}
foo2()
}
foo()
console.log(n) //err: n is not defined

В приведенном выше коде к внутреннему n нельзя получить доступ в глобальном, но к внешней функции можно получить доступ во вложенном внутреннем foo2(), который является специальным эффектом, создаваемым областью видимости.

Теперь, когда цепочка областей видимости понятна, давайте посмотрим на другой пример (очень запутанный, смотрите внимательно):

 var name = 'Mike'; //第一次定义name
  function showName() {
    console.log(name);  //输出 Mike 还是 Jay ?     
  }

  function changeName() {
    var name = 'Jay'; //重新定义name
    showName(); //调用showName()
  }
  changeName();

Как вы думаете, каков результат приведенного выше примера? Ответ Майк. Здесь мы вводим новую концепцию,词法作用域Существуют две модели охвата:

  • Лексическая область (статическая): поиск js осуществляется по коду书写时候的位置решать, не по положению на момент призыва
  • Динамическая область видимости: в настоящее время используются Perl и Bash (вы сами можете понять)

С помощью правил лексического объема мы можем снова проанализировать его.

  1. При вызове changeName() найдите эту функцию
  2. определить var name = "Джей"
  3. вызвать showName()
  4. Узнайте, есть ли метод showName() в changeName(), не найдите, посмотрите на внешний слой, найдите его
  5. Вызовите console.log(name), узнайте, есть ли имя внутри функции, нет, посмотрите, найдите его, name="Mike"
  6. вывод Майк

Закрытие

После понимания вышеуказанных знаний, наконец, пришел к закрытию
Официальное объяснение замыканий в двух книгах:

1. Маленькая «желтая» книжка (JavaScript вы не знаете): замыкания создаются, когда функция может запомнить и получить доступ к лексической области видимости, в которой она находится, даже если функция выполняется за пределами текущей лексической области видимости.
2. Красная книга (продвинутое программирование на JavaScript): замыкание означает доступ к другому функции переменных в области видимости функции

Очень абстрактная концепция, одно из моих собственных представлений:
Когда переменная (как имя выше) не является ни локальной переменной внутри функции, ни параметром функции, это свободная переменная (ссылающаяся на внешнюю переменную) относительно области действия, которая образует закрытый мешок.
Как сказать? Давайте посмотрим на демо, которое мы использовали в начале

let count = 500; //全局作用域
function foo1() {
  let count = 0; //函数全局作用域
  function foo2() {
    let count2 = 1; //随便新增一个变量
    // count++;  注释
    debugger;
    //console.log(count); 注释
    //return count;  注释
  }
  return foo2; //返回函数
}
let result = foo1();
result(); //结果为1
result(); //结果为2

Снова воспользовавшись браузером, мы обнаружим, что Замыкание исчезло, что также подтверждает то, что я сказал, если функция не вызовет внешнюю переменную, замыкание не будет сформировано, но если внешняя переменная будет вызвана, то сформируется замыкание Это означает, что不是所有的函数嵌套函数都能形成闭包

Наконец, давайте рассмотрим пример закрытия цикла.

for (var i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    debugger;
    console.log(i); // 输出什么?   
  }, 1000);
}

Ответ 6 6 6 6 6. Поскольку callback-функция в setTimeout является асинхронным процессом (асинхронность означает, что вам не нужно ждать, пока сначала выполнится мой код, вы можете выполнить его первым), а цикл for является синхронным ( код может выполняться только сверху вниз (исполнение вниз), выполняется немедленно, асинхронный setTimeout должен ждать одну секунду для выполнения, в это время я уже завершил цикл.
Есть три решения:

  1. Измените переменную в цикле for, чтобы позволить
for (let i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    debugger;
    console.log(i); // 1 2 3 4 5 
  }, 1000);
}

Таким образом, нет проблем, потому что let имеет функцию блочного уровня, и каждый уровень цикла независим и не влияет друг на друга, поэтому он может нормально выводить.
2. Поместите setTimeout() в функцию

for (var i = 1; i <= 5; i++) {
  log(i); // 1 2 3 4 5
}
function log(i) {
  setTimeout(function timer() {
    debugger;
    console.log(i);
  }, 1000);
}

Таким образом, эта функция также может быть достигнута.Принцип такой же, как и в первом методе.Каждый журнал() независим и не влияет друг на друга, так что мы можем получить правильные результаты.Вар потому что нет блока- уровень функции, будут проблемы. 3. Оберните это как анонимную функцию

for (var i = 1; i <= 5; i++) {
  (function (i) {
    setTimeout(function timer() {
      debugger;
      console.log(i);
    }, 1000);
  })(i)
}

Тот, что впереди(func..)определить функцию, а затем(i)Вызов, который в JavaScript называется функцией немедленного выполнения, на самом деле такой же, как и второй метод, но метод записи другой.

Эпилог

Понимание замыканий JavaScript — важный навык, который часто встречается на собеседованиях, и это единственный способ стать старшим инженером JavaScript.