Выразительный JavaScript (Хавербеке) - страница 26

>function power(base, exponent) {

>  if (exponent == undefined)

>    exponent = 2;

>  var result = 1;

>  for (var count = 0; count < exponent; count++)

>    result *= base;

>  return result;

>}


>console.log(power(4));

>// → 16

>console.log(power(4, 3));

>// → 64

В следующей главе мы увидим, как в теле функции можно узнать точное число переданных ей аргументов. Это полезно, т. к. позволяет создавать функцию, принимающую любое количество аргументов. К примеру, >console.log использует это свойство, и выводит все переданные ей аргументы:

>console.log("R", 2, "D", 2);

>// → R 2 D 2

Замыкания

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

Следующий пример иллюстрирует этот вопрос. В нём объявляется функция >wrapValue, которая создаёт локальную переменную. Затем она возвращает функцию, которая читает эту локальную переменную и возвращает её значение.

>function wrapValue(n) {

>  var localVariable = n;

>  return function() { return localVariable; };

>}


>var wrap1 = wrapValue(1);

>var wrap2 = wrapValue(2);

>console.log(wrap1());

>// → 1

>console.log(wrap2());

>// → 2

Это допустимо и работает так, как должно – доступ к переменной остаётся. Более того, в одно и то же время могут существовать несколько экземпляров одной и той же переменной, что ещё раз подтверждает тот факт, что с каждым вызовом функции локальные переменные пересоздаются.

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

С небольшим изменением мы превращаем наш пример в функцию, умножающую числа на любое заданное число.

>function multiplier(factor) {

>  return function(number) {

>    return number * factor;

>  };

>}


>var twice = multiplier(2);

>console.log(twice(5));

>// → 10

Отдельная переменная вроде >localVariable из примера с >wrapValue уже не нужна. Так как параметр – сам по себе локальная переменная.

Потребуется практика, чтобы начать мыслить подобным образом. Хороший вариант мысленной модели – представлять, что функция замораживает код в своём теле и обёртывает его в упаковку. Когда вы видите >return function(...) {...}, представляйте, что это пульт управления куском кода, замороженным для употребления позже.

В нашем примере