Говоря об анонимных функциях и замыканиях

внешний интерфейс JavaScript

предисловие

Я верю, что у многих фронтенд-партнеров будет более-менее контакт и понимание в работе и учебеанонимная функцияа такжеЗакрытие. Может быть, страдает от знаний, но и искать в Интернете много данных, информации и объяснений можно найти в их собственной риторике, и даже некоторые объяснения сами по себе не являются правильными, это больше головная боль. сегодня в чатанонимная функцияа такжеЗакрытие, расскажите о связи между ними (на самом деле прямой связи между ними нет! важно).

что такое анонимная функция

а такжеанонимная функцияСоответственно естьименованная функция, именованная функция очень проста: function myFn(){}, которая является именованной функцией Имя этой функции — myFn. Вы можете проверить это:

function myFn(){
}
cosnole.log(myFn.name);//myFn

В частности, выражения эталонных функций в версии es6 также можно рассматривать как именованные функции. Например, var myFn1 = function(){}, напечатав myFn1.name, также получит myFn1.

Кроме того, анонимные функции обычно выполняются сразу после использования анонимной функции. Часто называется самовыполняющимися анонимными функциями или самовызывающимися анонимными функциями. Обычно используется для создания режима песочницы, рольОткройте закрытую среду области видимости переменных, При совместной работе нескольких человек после слияния js кода не будет проблемы, что одни и те же переменные конфликтуют друг с другом. Есть много способов написать анонимные функции, которые выполняются немедленно, два самых распространенных:

(function(){ 
  console.log("我是匿名方式1");
})();//我是匿名方式1

(function(){ 
  console.log("我是匿名方式2");
}());//我是匿名方式2

console.log((function(){}).name);//'' name为空

Разница между ними заключается в следующем: во-первых, скобки, инициирующие выполнение, находятся за пределами скобок анонимной функции, а во-вторых, скобки, инициирующие выполнение, находятся внутри анонимной функции. Фактический метод записи рекомендуется для первого.Этот метод записи больше соответствует механизму вызова, и параметры при вызове также более очевидны, а именно:

(function(i,j,k){ 
  console.log(i+j+k);
})(1,3,5);
//9

Существуют и другие способы написания самовыполняющихся анонимных функций, а именно:

-function(){ 
  console.log("我是匿名方式x");
}();
console.log(-function(){}.name);//-0
+function(){ 
  console.log("我是匿名方式x");
}();
console.log(+function(){}.name);//0
~function(){ 
  console.log("我是匿名方式x");
}();
console.log(~function(){}.name);//-1
!function(){ 
  console.log("我是匿名方式x");
}();
console.log(!function(){}.name);//true
void function(){ 
  console.log("我是匿名方式x");
}();
console.log(void function(){}.name);//undefined

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

Фактически,Анонимная функция, которая выполняется немедленно, не является функцией, так как он был выполнен, это результат, но результатом может быть строка, число, значение null/false/true, объект, массив или функция (и объекты, и массивы могут содержать функции), Результат в основном зависит от того, что функция возвращает после завершения выполнения.

Как определяется замыкание и как его понимать

Само определение замыкания относительно абстрактно, MDN официально поясняет, что:A closure is the combination of a function and the lexical environment within which that function was declared.Китайское объяснение:Замыкание — это комбинация функции и лексического окружения, в котором эта функция была определена.Утверждение можно увидеть во многих местах: каждая функция в js является замыканием, поэтому нет проблем с пониманием этого, но это усложнит понимание замыкания Поймите это: это возможность выполнять методы, определенные внутри функция вне функции и доступ к переменным, определенным внутри этой функции.

Здесь давайте рассмотрим классический случай использования замыканий для доступа к локальным переменным внутри функции вне функции:

function box(){
  var a = 10;
  function inner(){
    return a;
  }
  return inner;
}
var outer = box();
console.log(outer());//10

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

Но посмотрите здесь, или внешний вид соотношения Монголии, где использование закрытий? Похоже на трехстороннюю функцию, коробку, внутреннюю и внешнюю = коробку ().

  • Закрытие, используемое в этом случае, на самом делеЛексическая среда, в которой определяются внутреннее и внутреннее, на замыкание ссылается внешний внешний объект после его возврата, поэтому внутренний может выполняться вне блока, а внутренний может читать переменную a внутри блока.

  • Целью использования этого замыкания является доступ к нестандартному объекту путем выполнения external().

Реализация замыканий с анонимными функциями

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

//第一步直把内部inner这个具名函数改为匿名函数并直接return, 结果同样是10
function box(){
  var a = 10;
  return function(){
    console.log(a) ; 
  }
}
var outer = box();
outer();//10
//第二步把外部var outer = box()改成立即执行的匿名函数
var outer = (function(){
  var a=10;
  return function(){
    console.log(a);
  }
})();
//outer 作为立即执行匿名函数执行结果的一个接收,这个执行结果是闭包,outer等于这个闭包。
//执行outer就相当于执行了匿名函数内部return的闭包函数
//这个闭包函数可以访问到匿名函数内部的私有变量a,所以打印出10
outer();//10

Таким образом, мы переписываем замыкание, внедренное в анонимную функцию, чтобы фактически использовать окружающую среду, когда закрытие является функцией возврата и функции внутреннего определения. Итак, это могло:Замыкания не имеют прямого отношения к тому, является ли функция анонимной или нет., как анонимные, так и именованные функции могут создавать замыкания.

Проблемы с петлями и решения

Также есть непонятная проблема, которая часто встречается в работе и учебе в цикле for:

for(var i = 0;i<5;i++){
  setTimeout(function(){
    console.log(i);
  },100*i);
}

Мы хотим напечатать 0,1,2,3,4, но он печатает 5 5s, что смущает. Что вызывает эту проблему? Это связано с тем, что функция обратного вызова setTimeout не выполняется немедленно, но не начнет отсчет времени и выполнение до конца цикла (механизм выполнения здесь не растягивается).Функции в js сохраняют только ссылки на переменные перед выполнением, фактически не получает и не сохраняет значение переменной. Таким образом, после завершения цикла значение i уже равно 5, поэтому функция обратного вызова, которая запускает таймер, напечатает 5 5s.

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

for(var i = 0;i<5;i++){
  (function(i){
    setTimeout(function(){
      console.log(i);
    },100*i);
  })(i);
}

Ожидаемые результаты можно получить: 0, 1, 2, 3, 4. В настоящее время многие думают, что эта анонимная функция, которая выполняется сразу, является замыканием, на самом деле это понимание неверно, и тогда многие случаи расширяются. вдобавок к неправильному пониманию. , что приводит других в замешательство и недоумение. Приложен скриншот ответа одноклассника на Stack Overflow, Я думаю, что то, что он сказал, особенно разумно:

Оригинальный адрес:stackoverflow.com/questions/8…

2) Так что же такое замыкание в этом цикле for и какую роль играет самовыполняющаяся анонимная функция?Мы можем попытаться переписать эту самовыполняющуюся анонимную функцию в именованную функцию, чтобы проверить результат:

for(var i = 0;i<5;i++){
  function hasNameFn(i){
    setTimeout(function(){
      console.log(i);
    },100*i);
  };
  hasNameFn(i);
}

Его можно найти, и результат использования анонимной функции тот же, поэтому он также может иллюстрировать, что замыкание не имеет прямого отношения к анонимной функции.

Как понять замыкание в этом цикле for и роль самовыполняющейся анонимной функции:

  • Этот цикл for фактически генерирует замыкание, когда выполняется функция обратного вызова таймера.Средой выполнения этих функций обратного вызова является окно., аналогично среде выполнения глобальной внешней функции, которая ссылается на внутреннюю в только что приведенном примере, а анонимная функция эквивалентна функции box в только что приведенном примере.

  • Роль самовыполняющейся анонимной функции также очень проста: она создает приватное лексическое окружение каждый раз, когда выполняется цикл, а i текущего цикла передается во время выполнения и сохраняется в этом лексическом окружении. к объявлению var в приведенной выше функции box локальная переменная a.

  • Я только что упомянул, что функции перед выполнением сохраняют только ссылки на переменные.Самозапускающиеся анонимные функции выполняются именно потому, что, поэтому можно получить текущее значение переменной i. Следовательно, i, на которое ссылается функция обратного вызова таймера во время выполнения, уже определил конкретное значение.

  • Может быть, мы можем переписать его, чтобы сделать его более понятным:

for(var i = 0;i<5;i++){
  (function(j){
    var _i = j;
    setTimeout(function(){
      console.log(_i);
    },100*_i);
  })(i);
}

Переписанный параметр анонимной функции представлен j, а локальная переменная _i=j определена внутри. Когда выполняется анонимная функция, в цикле передается i. В это время _i, напечатанное в таймере, на самом деле равно j. Анонимная функция выполняется немедленно, и значение j также определяется. Следовательно, результат, выводимый функцией обратного вызова каждого таймера, также является значением, которое было определено анонимной функцией.

3) Другие решенияЧтобы решить проблему с циклом for прямо сейчас, нужно решить фундаментальную проблему, как сделать так, чтобы функция обратного вызова таймера каждого цикла ссылалась на текущий i вместо i после завершения цикла.

Самый простой способ сделать это — использовать es6 let, который создает блочную область для переменных:

for(let i = 0;i<5;i++){
  setTimeout(function(){
    console.log(i);
  },100*i);
}
//改写成下面这么写更好理解一些
for(var i = 0;i<5;i++){
  let j = i;
  setTimeout(function(){
    console.log(j);
  },100*j);
}

Вы также можете использовать bind для привязки текущего i к функции обратного вызова таймера (на самом деле метод bind по-прежнему реализует каррированное закрытие для вызывающей стороны и сохраняет параметры, переданные вызывающей стороне во время выполнения):

for(var i = 0;i<5;i++){
  setTimeout(function(i){
    console.log(i);
  }.bind(this,i),100*i);
}

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

Пока взаимосвязь между анонимными функциями и замыканиями почти одинаковая.Надеюсь оказать некоторую помощь тем,кто запутался в анонимных функциях и замыканиях.В то же время в статье есть недочеты,прошу указать,совместно изучаем прогресс!