Function Declaration объявленная в цикле JS

316
07 декабря 2017, 02:10

Здравствуйте,помогите разобраться почему Function Declaration объявленная в цикле,создается на каждой итерации.

Из https://learn.javascript.ru/function-declaration-expression

Основное отличие между ними: функции, объявленные как Function Declaration, создаются интерпретатором до выполнения кода.

var arr = []; 
for (var i = 0; i < 5; i++) { 
  function example() {} 
  example.i = i; 
  arr.push(example); 
} 
console.log(arr);

Вопрос возник когда разбирался с NFE которые создаются в процессе выполнения выражения.

Answer 1

На самом деле в конкретном примере нет абсолютно никакой разницы, используется ли функциональное объявление или функциональное выражение:

var arr = []; 
for (var i = 0; i < 5; i++) { 
  function example() {} 
  example.i = i; 
  arr.push(example); 
} 
 
console.log(arr[0] === arr[1]); // === arr[2] === ... => false

var arr = []; 
for (var i = 0; i < 5; i++) { 
  var f = function example() {}; 
  f.i = i; 
  arr.push(f); 
} 
 
console.log(arr[0] === arr[1]); // === arr[2] === ... => false

В обоих случаях идентификатор будет перезаписываться. В первом случае, с Function Declaration, вы просто на каждой итерации цикла заново объявляете функцию example. Во втором же случае вы каждый раз заново определяете (переопределяете) переменную f. И в обоих случаях в массив записывается та функция, которая только что была объявлена.

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

Здравствуйте,помогите разобраться почему Function Declaration объявленная в цикле,создается на каждой итерации.

Встречный вопрос: а какого поведения вы могли ожидать?

Воспроизведите этот код:

function example() { 
  return 1; 
} 
 
function example() { 
  return 2; 
} 
 
function example() { 
  return 3; 
} 
 
function example() { 
  return 4; 
} 
 
function example() { 
  return 5; 
} 
 
console.log(example());

В этом примере я реализовал то, что мог бы реализовать циклом, но сделал это вручную. Значение example равняется последней объявленной функции с этим именем. Проще говоря, example просто перезаписывается.

Абсолютно то же самое происходит и внутри цикла. На первой итерации вы определили функцию и добавили ее в массив. На второй итерации вы переопределили ранее определенную функцию и добавили уже переопределенную функцию в массив. И так далее.

Что касается этой части:

Основное отличие между ними: функции, объявленные как Function Declaration, создаются интерпретатором до выполнения кода.

Тут, вероятно, речь идет про, так называемый, Variable Hoisting. Про него уже достаточно много сказано, но, чтобы ответ соответствовал ответу, приведу цитату из книги С. Стоянова, которая максимально точно отражает происходящее "под капотом" (перевод под оригиналом):

To avoid hoisting always use a function expression.

// antipattern 
// for illustration only 
// global functions 
function foo() { 
  alert('global foo'); 
} 
 
function bar() { 
  alert('global bar'); 
} 
 
function hoistMe() { 
  console.log(typeof foo); // "function" 
  console.log(typeof bar); // "undefined" 
  foo(); // "local foo" 
  bar(); // TypeError: bar is not a function 
  // function declaration: 
  // variable 'foo' and its implementation both get hoisted 
 
  function foo() { 
    alert('local foo'); 
  } 
  // function expression: 
  // only variable 'bar' gets hoisted 
  // not the implementation 
  var bar = function() { 
    alert('local bar'); 
  }; 
} 
hoistMe();

In this example you see that, just like with normal variables, the mere presence of foo and bar anywhere in the hoistMe() function moves them to the top, overwriting the global foo and bar.

Перевод

Чтобы избежать хоистинга, всегда используйте функциональные выражения (Function Expressions)

// антипаттерн 
// только для примера 
// глобальные функции 
function foo() { 
  alert('global foo'); 
} 
 
function bar() { 
  alert('global bar'); 
} 
 
function hoistMe() { 
  console.log(typeof foo); // "function" 
  console.log(typeof bar); // "undefined" 
  foo(); // "local foo" 
  bar(); // TypeError: bar is not a function 
  // function declaration: 
  // переменная 'foo' и ее реализация "хоистятся" 
 
  function foo() { 
    alert('local foo'); 
  } 
  // function expression: 
  // только переменная 'bar' "хоистится", 
  // не ее реализация 
  var bar = function() { 
    alert('local bar'); 
  }; 
} 
hoistMe();

В этом примере вы можете видеть, что, также, как и с "обычными" переменными, только лишь наличие foo или bar где угодно внутри функции hoistMe() перемещает их вверх функции, перезаписывая [ранее определенные] глобальные foo и bar.

Answer 2

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

var arr = []; 
 
try { 
    example(); 
} catch (err) { 
    console.log("error: "+err) 
} 
 
for(var i = 0; i < 5; i++) { 
    function example(){console.log("func init #"+i)} 
    example.i = i; 
    arr.push(example); 
} 
 
console.log(example.i); 
example();

READ ALSO
JS как вызвать функцию из массива?

JS как вызвать функцию из массива?

Добрый день, у меня есть глобальный массив callbacks, внутри него хранится функция, которую мне надо исполнить внутри событияon("click")

321
cannot read property of undefined в React

cannot read property of undefined в React

Например есть метод:

275
Подключение сбербанк эквайринг

Подключение сбербанк эквайринг

Здравствуйте! Пытаюсь сделать оплату на сайте через сбербанкЕсть необходимые данные для тестирования, полученные от сбербанк (пароль и логин...

319
Откуда берутся лишние кавычки?

Откуда берутся лишние кавычки?

Запрос выглядит так:

239