Please note, this is a STATIC archive of website developer.mozilla.org from 03 Nov 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Revision 1123783 of Функции

  • URL ревизии: Web/JavaScript/Guide/Functions
  • Заголовок ревизии: Функции
  • ID ревизии: 1123783
  • Создано:
  • Автор: Mainstand
  • Это текущая ревизия? Нет
  • Комментарий

Содержание ревизии

{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Loops_and_iteration", "Web/JavaScript/Guide/Expressions_and_Operators")}}

Функции в JavaScript

Функции - ключевая концепция в JavaScript. Важнейшей особенностью языка является {{interwiki("wikipedia","Функции_первого_класса", "первоклассная поддержка функций")}}​ (functions as first-class citizen). Любая функция это объект, и следовательно ею можно манипулировать как объектом, в частности:

  • передавать как аргумент и возвращать в качестве результата при вызове других функций ({{interwiki("wikipedia","Функция_высшего_порядка", "функций высшего порядка")}}).
  • создавать анонимно и присваивать в качестве значений переменных или свойств объектов

Это определяет высокую выразительную мощность JavaScript и позволяет относить его к числу языков, реализующих {{interwiki("wikipedia","Функциональное_программирование", "функциональную парадигму программирования")}} (что само по себе есть очень круто по многим соображениям).

Функция в JavaScript
специальный тип объектов, позволяющий формализовать средствами языка определённую логику поведения и обработки данных. 

Для понимания работы функций необходимо (и достаточно?) иметь представление о следующих моментах:

  • способы объявления 
  • способы вызова 
  • параметры и аргументы вызова (arguments)
  • область данных (Scope) и замыкания (Closures)
  • объект привязки (this)
  • возращаемое значение (return)
  • исключения (throw)
  • использование в качестве конструктора объектов
  • сборщик мусора (garbage collector)

Объявление функций

Функция (как и всякий объект) должна быть объявлена(определена, define) перед её использованием. 

Объявление(определение) функции - указание сигнатуры и тела функции:

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

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

Во избежание оговорок о глобальных переменных и функциях, удобно полагать, что программа на языке JavaScript представляет собой тело неявной функции [[ main]]().

Существует три способа объявить функцию:

В декларативном стиле

Для декларативного объявления функции используется синтаксическая конструкция

function идентификатор(параметры) {

  инструкции

  return выражение

}
  • Ключевое слово function
  • Идентификатор (обязательно).
  • Список имён формальных параметров (и значений по умолчанию) в круглых скобках разделенных запятыми
  • тело функции в фигурных скобках вида {}.

Пример. Следующий код объявляет функцию  с именем square и параметром number; тело состоит из инструкции return и выражения, которое дословно формализуют следующую логику: "вернуть результат произведения аргумента  number на самого себя". :

function square(number) {

  return number * number;
}

Особенностью декларативного объявления является его "всплытие"(hoisting) в начало функции, независимо от того в каком месте контейтера оно находится. 

В примере ниже декларативное объявление функции находится после вызова:

{
  print(square(5));

  // инициализация "всплывает" вместе с декларацией переменной square
  // Аналогичный код в функциональном стиле работать не будет
  function square(n){return n*n}
} 

В функциональном стиле

Функции также могут быть созданы внутри выражения. Такие функции, как правило, анонимны:

var square = function(number) {
  return number * number;
}

Но могут иметь определённое имя. (это имя удобно использовать для рекурсии, или в отладчике(debugger)):

var factorial = function fac(n) {return n<2 ? 1 : n*fac(n-1)};

print(factorial(3));

Cтрелочные функции

Современный стандарт языка поддерживает аномимные стрелочные функции(fat arrow function).

(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression

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

[0, 1, 2, 5, 10].map((x) => x * x * x); // вернет [0, 1, 8, 125, 1000].

Помимо упрощённого синтаксиса, такие функции всегда неявно привязываются в МОМЕНТ ОБЪЯВЛЕНИЯ к текущему лексическому контексту выполнения:

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // в данном случае this будет ссылаться на создаваемый объект obj, а не на window
  }, 1000);
}

var obj = new Person();

Полное описание в статье справочника {{jsxref("Functions/Arrow_functions")}}

Смотри также статью на hacks.mozilla.org: "ES6 In Depth: Arrow functions".

В стиле ООП

Учитывая то, что функция по сути является объектом, можно использовать оператор new и Function конструктор чтобы создавать функции динамически во время выполнения (подобно тому как это делает eval()). 

Однако такого подхода следует избегать из соображений производительности и безопасности.

var powerOfFive = new Function('x',
                  'return ' + Array(5).map(()=>'x').join('*'));

Методы

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

Метод 
это функция, заданная как значение свойства объекта.

Специальный синтаксис вызова методов позволяет неявно передавать объект в качестве привязки(this).

class Greeting{

  constructor(prefix){
    this.prefix = prefix;
  }

  // это метод:
  hello(name){
    return `${this.prefix}, ${name}`;
  }
}

var obj = new Greeting("Привет");

// вызов метода (obj передаётся в качестве контекста `this`)
obj.hello('Вася');

Больше информации об объектах и методах в Работа с Объектами.

Вызов функций

Помимо манипуляций c ними как с обычными объектами, функции можно вызывать - запустить процесс вычисления выражений над данными

Вызов (call) функции
последовательное выполнение управляющий инструкций и выражений из тела функции применительно к входным данным и контексту. 
Выражение (expression) 
комбинация математических и специальных операций, а также вызовов функций, которые описывают способ вычисления результирующего значения(result) в зависимости от входящих данных(input). 

В момент вызова фунции могут быть переданы 

  • входные данные в виде списка аргументов-значений,
  • а также объект привязки - специальный аргумент, именуемый ключевым словом this.

Кроме этого, выражения в функции могут адресоваться:

  • к константам (строковые литералы, числа и т.д.)
  • к локальным переменным,
  • а также к внешним свободным переменным по цепочке замыкания.

В результате выполнения функция

  • возвращает некоторое значение (явно с помощью return или неявно) 
  • или бросает исключение.

Например, вызвать  square() можно следующим образом:

// Функция выполняет свои инструкции над аргументом 5 
// и возвращает значение-результат 25

var result = square(5);

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

Рекурсия

Рекурсивная функция 
функция, содержащая вызов самой себя непосредственно в своём теле либо через другие функции.
Классический пример рекурсивной функции вычисляющей факториал:
function factorial(n) {
  if ((n == 0) || (n == 1))
    return 1;
  else
    return (n * factorial(n - 1));
}

//вычислить факториал пяти:
var e = factorial(5); // e будет равно 120

Вызов с помощью .apply() и .call()

Существуют и другие способы вызывать функции. Поскольку функции сами являются объектами, они содержат собственные методы. В частности, метод apply(), может использоваться, например, когда нужно адресоваться к функции динамически, или передать различное количество аргументов, или явно указать контекст.

var fn = resolveAction();
fn.apply(context, [number1, number2]);

Смотрите Function объект для более детального понимания.

Параметры и аргументы вызова

Параметры функции
список идентификаторов (и возможно сопоставленных им значений по умолчанию и типов данных), заданный В МОМЕНТ ОБЪЯВЛЕНИЯ.
Параметры удобно рассматривать как объявления локальных переменных, каждая из которых инициализируются в МОМЕНТ ВЫЗОВА соответствующим значением из списка аргументов (или значением по умолчанию).
Аргументы(входные данные) функции
произвольный список значений, передаваемых функции В МОМЕНТ ВЫЗОВА.

Соответствие имён параметров и значений аргументов задано тупо порядком их перечисления при обявлении/вызове.

Параметры в  Javascript никак не ограничивают ни количество, ни содержание аргументов.

 

По значению или по ссылке

Типы аргументов функции могут быть как примитивами (строки, числа, логические(boolean)), так и объектами (включая Array или функции):

  • Значения-примитивы передаются в функции по значению: значение КОПИРУЕТСЯ, так что если функция изменит значение параметра, это изменение не будет иметь внешнего эффекта.
  • объекты передаются в фунцию по ссылке:  переприсваивание самой ссылки также не имеет внешнего эффекта, НО если функция изменяет свойства объекта, то эти изменения будут видимы вне функции (побочный эффект).

пример:  Стрёмный побочный эффект от изменения свойств объекта-аргумента:

function myFunc(patient) {
  patient.gender= "F";
}

var he = {gender: "M"};

myFunc(he);

var y = he.gender;     // y gets the value "F", oops

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

function myFunc(theObject) {
  theObject = {make: "Ford", model: "Focus", year: 2006};
}

var mycar = {make: "Honda", model: "Accord", year: 1998},
    x,
    y;

x = mycar.make;     // x gets the value "Honda"

myFunc(mycar);
y = mycar.make;     // y still gets the value "Honda" 

В первом случае, объект mycar был передан в функцию myFunc, которая изменила его состояние. Во втором случае, функция не изменяла переданный объект; вместо этого, она создавала новую локальную переменную с тем же именем что и имя параметра функции, так что никак не влияет на глобальный объект передаваемый в функцию.

Использование объекта arguments

Доступ к аргументам может быть получен непосредственно (без обращения по именам объявленных параметров).

Ключевое слово arguments адресует структуру, где содержится список входных аргументов в порядке передачи при вызове. Общее количество аргументов содержится в arguments.length, а значение отдельного аргумента можно получить как arguments[i], где i - порядковый номер аргумента начиная с нуля. Таким образом, например, первый аргумент переданный в функцию будет arguments[0]. .

Используя объект arguments, можно адресоваться к большему количеству аргументов, чем это описано в параметрах функции. Это часто полезно, если вы не знаете заранее как много аргументов будет передаваться в функцию. 

Заметьте, что структура arguments крякает как утка похожа на массив, но массивом не является.

Рассмотрим, например, функцию которая объединяет произвольное количество строк. Единственный аргумент параметр в определении функции - это строка-разделитель:

function myConcat(separator) {
   var result = ""; // initialize list
       
   // iterate through arguments
   for (var i = 1; i < arguments.length; i++) {
      result += arguments[i] + separator;
   }
   return result;
}

// returns "red, orange, blue, " 
myConcat(", ", "red", "orange", "blue"); 

// returns "elephant; giraffe; lion; cheetah; " 
myConcat("; ", "elephant", "giraffe", "lion", "cheetah"); 

Аргументы и оператор развёртки

Современный стандарт языка позволяет в элегантной функциональной манере отказаться от arguments, заменив его оператором развёртки(spread) (...) :

const myConcat = (sep, ...strings) => strings.join(sep) // !!!

Значения по умолчанию

В JavaScript, значения параметров по умолчанию устанавливаются в undefined.

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

function multiply(a, b) {
  b = typeof b !== 'undefined' ?  b : 1;

  return a*b;
}

multiply(5); // 5

Современный стандарт языка вводит прекрасную возможность явного указания значения по умолчанию с помощью выражения при ОБЪЯВЛЕНИИ функции:

function multiply(a, b = 1) {
  return a*b;
}

multiply(5); // 5

Заметим что вычисление значения по умолчанию будет происходить В МОМЕНТ ВЫЗОВА функции 

Также будет нелишним заметить что такие выражения могут адресоваться к предыдущим параметрам:

function multiply(a, b = 2*a) { return a*b; } 

multiply(5); // 50

Детальная информация в статье справочника default parameters 

Область данных и замыкания

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

Для этих целей в JavaScript реализован  механизм разрешения имён на основе области данных(Scope) и замыканий(Closure):

var prefix = "Привет";
function sayHi(name) {
  var phrase = prefix + ", " + name;
  alert( phrase );
}
// логика разрешения переменных
RESOLVE = (env, id) => {
 if (env[id]) return env[id];
 if (env.OUTER_SCOPE) return RESOLVE(env.OUTER_SCOPE, id);
 throw new Error('ReferenceError: variable %id is not declared')
}
...
// внешний область данных, актуальный на момент создания функции
SCOPE = { prefix: "Привет" , sayHi: undefined} 
...
// замыкание = функция + внешнее окружение
var sayHi = { 
 OUTER_SCOPE: SCOPE, //захвачен в момент создания функции
 PARAMETERS:['name'] 
 BODY: ()=>{

  // внутренняя область данных, создаваемая неявно в момент вызова
  SCOPE = { 
    name: 'Вася', 
    phrase: undefined,
    OUTER_SCOPE: sayHi.OUTER_SCOPE // образуем цепочку 
  }

  var phrase = 
             RESOLVE(SCOPE,'prefix') // будет извлечён из внешней области
             + ", "
             + RESOLVE(SCOPE,'name'); // будет извлечён из внутренней области

  alert( RESOLVE(SCOPE,'phrase' );

 }
}

// передаётся контекст null и список аргументов
CALL_FUNCTION(sayHi, null, ['Вася']);

1) Каждый раз В МОМЕНТ ВЫЗОВА функции, происходит неявное создание нового экземпляра области данных(Scope) - специального объекта , где ДО НАЧАЛА ВЫПОЛНЕНИЯ(hoisting) регистрируются

  • переменные из списка параметров функции, проинициализированные соответствующими аргументами-значениями.
  • переменные, явно объявленные в теле функции с помощью var
  • вложенные функции, объявленные в теле в декларативном стиле
  • неявная ссылка-замыкание[[OuterScope]] данной функции (образуя цепочку замыканий лексического диапазона)

2) В МОМЕНТ ОБЪЯВЛЕНИЯ объект-функция неявно получает постоянную ссылку [[OuterScope]] на актуальную область переменных  выполняющейся функции-контейнера.

Эта ссылка (или в более общем смысле - связка функция + ссылка) называется замыканием. Она позволяет адресоваться из тела функции  к внешним(свободным) переменным.

3) Если искомое имя переменной не обнаружено в собственной области переменных, то поиск продолжается последовательно вдоль по цепочке замыканий(scope chain) пока имя не будет найдено (в противном случае сработает исключение  ReferenceError).

В спецификации ECMA-262,  присутствует гораздо больше деталей, в частности речь идёт о двух объектах: VariableEnvironment и LexicalEnvironment. Но мы абстрагируемся и используем везде термин Scope, это позволяет достаточно точно описать происходящее.

Более формальное описание находится в спецификации ECMA-262, секции 10.2-10.5 и 13.

Таким образом, общее правило видимости и доступности переменных таково

 Локальные переменные и параметры функции, видны ТОЛЬКО в пределах этой функции и внутри всех вложенных функций на всю глубину.

Они доступны на протяжении всего времени существования функций, которые к ним обращаются.

Замыкание продожает существовать пока существует сама функция (несмотря на то, что внешняя функция возможно уже завершила выполнение и снесена сборщиком мусора).

Таким образом, время жизни локальных переменных функции определяется временем выполнения/использования этой функции и всех вложенных функций.

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

пример:  Вложенная функция incrementor обращается к параметру base внешней функции factory (замыкание продолжает существовать после завершения внешней функции):

function factory (base) {
  
  // returning function has access to external `base` parameter  
  return function incrementor(inc) { return base + inc; };
}

var sum2 = factory(2);

sum2(5); // Returns 7

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

var createPet = function(name) {
  var sex;
  
  return {
    setName: function(newName) {
      name = newName;
    },
    
    getName: function() {
      return name;
    },
    
    getSex: function() {
      return sex;
    },
    
    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie"); 
pet.setSex("female"); 
pet.getName(); // Vivie
pet.getSex(); // female 

var pet2 = createPet("Oliver"); 
pet2.setSex("male"); 
pet2.getName(); // Oliver
pet2.getSex();  // male
                  

Переменные внутренней функции могут использоваться в качестве безопасных хранилищ для закрытых(private) данных:

var getCode = (function(){
  // никто не попробует сахарку
  var secureCode = "сахарок";
​ 
  return function (token) {
    return mix(secureCode, token);
  };
})();

getCode();    // Returns the secret code

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

Пример со перекрытием имён переменных внешней и внутренней функций

var createPet = function(name) {  // Outer function defines a variable called "name"
  return {
    setName: function(name) {    // Enclosed function also defines a variable called "name"
      name = name;               // ??? How do we access the "name" defined by the outer function ???
    }
  }
}

Объект привязки (this)

объект привязки 
специальный аргумент, именуемый ключевым словом this.

Основное назначение объекта привязки состоит в поддержке вызовов методов объектов в стиле ООП (см. выше).

// для методов объекта 
obj.method(param); // ->  obj.method.call(obj, param)

// или для внешних функций:
var obj = {}, fn = function(){};
obj::fn(param) // -> fn.call(obj, param)

Ссылка на объект привязки this передаётся

  • для обычных функций - каждый раз В МОМЕНТ ВЫЗОВА.
  • для стрелочных функций - связывается в МОМЕНТ ОБЪЯВЛЕНИЯ

Стандарт языка предоставляет средства явного связывания 

var obj = { some : function (){...} }

var boundSome = some.bind(obj);

// или ещё проще:

var boundSome2 = ::obj.some;

Далее

Подробное техническое описание функций в статье справочника {{jsxref("Functions","Функции")}}

Смотрите также Function в Справочнике JavaScript для получения дополнительной информации по функции как объекту.

Внешние ресурсы:

{{ autoPreviousNext("JSGChapters") }}

Источник ревизии

<p>{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Loops_and_iteration", "Web/JavaScript/Guide/Expressions_and_Operators")}}</p>

<h2 id="Функции_в_JavaScript">Функции в JavaScript</h2>

<p>Функции - ключевая&nbsp;концепция&nbsp;в JavaScript.&nbsp;Важнейшей особенностью языка является {{interwiki("wikipedia","Функции_первого_класса", "первоклассная поддержка функций")}}​<em> (functions as first-class citizen).</em>&nbsp;Любая <strong>функция это объект,</strong> и следовательно ею&nbsp;можно манипулировать как объектом, в&nbsp;частности:</p>

<ul>
 <li>передавать как аргумент&nbsp;и возвращать в качестве результата при вызове&nbsp;других функций ({{interwiki("wikipedia","Функция_высшего_порядка", "функций высшего порядка")}}).</li>
 <li>создавать анонимно и&nbsp;присваивать в качестве&nbsp;значений переменных или&nbsp;свойств объектов</li>
</ul>

<p>Это определяет высокую выразительную мощность JavaScript&nbsp;и позволяет относить его&nbsp;к числу языков, реализующих&nbsp;{{interwiki("wikipedia","Функциональное_программирование", "функциональную парадигму программирования")}} <sub>(что само по себе есть очень круто по многим соображениям)</sub>.</p>

<dl>
 <dt>Функция в JavaScript</dt>
 <dd>специальный&nbsp;тип объектов, позволяющий формализовать средствами языка&nbsp;определённую&nbsp;<em>логику</em>&nbsp;поведения и&nbsp;обработки данных.&nbsp;</dd>
</dl>

<p>Для понимания работы функций необходимо (и достаточно?) иметь представление о следующих моментах:</p>

<ul>
 <li>способы <a href="#definition">объявления</a>&nbsp;</li>
 <li>способы&nbsp;<a href="#call">вызова</a>&nbsp;</li>
 <li>параметры и аргументы вызова (<code>arguments</code>)</li>
 <li>область данных&nbsp;<code>(</code>Scope<code>) и&nbsp;</code>замыкания (Closures)</li>
 <li>объект привязки (<code>this)</code></li>
 <li>возращаемое значение (<code>return</code>)</li>
 <li>исключения (<code>throw</code>)</li>
 <li>использование в качестве конструктора объектов</li>
 <li>сборщик мусора <code>(garbage collector)</code></li>
</ul>

<h2 id="Объявление_функций"><a id="definition" name="definition"></a>Объявление функций</h2>

<ul>
</ul>

<p>Функция <sub>(как и всякий объект)</sub>&nbsp;должна быть <em>объявлена(определена, define)&nbsp;</em>перед её использованием.&nbsp;</p>

<p><strong>Объявление(определение)&nbsp;функции</strong> - указание сигнатуры и тела функции:</p>

<ul>
 <li><strong>сигнатура</strong> - имя(необязательно) и&nbsp;список входных формальных параметров&nbsp;</li>
 <li><strong>тело функции</strong> -&nbsp;комбинация управляющих конструкций и&nbsp;выражений языка над внешними и локальными данными</li>
</ul>

<p>Объявление пользовательской функции всегда находится в теле некоторой внешней функции-контейнера, которая, в свою очередь, возможно также вложена в некоторую функцию. Цепочка всех таких внешних функций, вложенных друг в друга, образует <strong>лексический диапазон функции.&nbsp;</strong></p>

<div class="note">
<p>Во избежание оговорок о глобальных переменных и функциях,&nbsp;удобно полагать,&nbsp;что программа на языке JavaScript представляет собой тело неявной функции [[&nbsp;<code>main]]().</code></p>
</div>

<p>Существует три способа объявить функцию:</p>

<h3 id="В_декларативном_стиле">В&nbsp;декларативном стиле</h3>

<p>Для декларативного объявления функции&nbsp;используется синтаксическая конструкция</p>

<pre>
function идентификатор(параметры) {

  инструкции

  return выражение

}</pre>

<ul>
 <li>Ключевое слово <code><a href="/en-US/docs/JavaScript/Reference/Statements/function" title="function">function</a></code></li>
 <li>Идентификатор (обязательно).</li>
 <li>Список имён формальных параметров (и значений по умолчанию) в круглых скобках&nbsp;разделенных запятыми</li>
 <li>тело функции в фигурных скобках вида <code>{}</code>.</li>
</ul>

<p><strong><u>Пример.</u></strong>&nbsp;Следующий код объявляет функцию&nbsp; с именем&nbsp;<code>square&nbsp;и параметром<font face="Open Sans, Arial, sans-serif">&nbsp;</font></code><code>number;</code>&nbsp;тело состоит из инструкции <code><a href="/en-US/docs/JavaScript/Reference/Statements/return" title="return">return</a>&nbsp;</code>и выражения, которое&nbsp;дословно формализуют следующую логику: "вернуть результат произведения&nbsp;аргумента&nbsp;&nbsp;<code>number</code>&nbsp;на самого себя".&nbsp;:</p>

<pre class="brush: js">
function square(number) {

  return number * number;
}

</pre>

<div class="note">
<p>Особенностью декларативного объявления является его "всплытие"(hoisting) в начало функции, независимо от того в каком месте контейтера&nbsp;оно находится.&nbsp;</p>
</div>

<p>В примере ниже&nbsp;декларативное&nbsp;объявление функции находится после вызова:</p>

<pre function="Syntax.JavaScript">
{
  print(square(5));

  // инициализация "всплывает" вместе с декларацией переменной square
  // Аналогичный код в функциональном стиле&nbsp;работать не будет
  function square(n){return n*n}
} 
</pre>

<h3 id="В_функциональном_стиле">В функциональном стиле</h3>

<p>Функции также могут быть созданы внутри&nbsp;<em>выражения</em>.&nbsp;Такие функции, как правило,&nbsp;<strong>анонимны</strong>:</p>

<pre class="brush: js">
var square = function(number) {
  return number * number;
}
</pre>

<p>Но&nbsp;могут иметь определённое имя. (это имя&nbsp;удобно использовать&nbsp;для рекурсии, или в отладчике(debugger)):</p>

<pre class="brush: js">
var factorial = function <strong>fac</strong>(n) {return n&lt;2 ? 1 : n*<strong>fac</strong>(n-1)};

print(factorial(3));
</pre>

<h4 id="Cтрелочные_функции"><strong>Cтрелочные функции</strong></h4>

<p>Современный стандарт языка поддерживает аномимные&nbsp;<strong>стрелочные функции(fat arrow function)</strong>.</p>

<pre>
<strong>(</strong><em>param1</em>, <em>param2</em>, …, <em>paramN</em><strong>) =&gt; {</strong> <em>statements</em> <strong>}
</strong><strong>(</strong><em>param1</em>, <em>param2</em>, …, <em>paramN</em><strong>) =&gt;</strong> <em>expression</em>

</pre>

<p id="Advanced_Syntax">Они&nbsp;особенно&nbsp;удобны, когда надо задать аргумент для функции высшего порядка:</p>

<pre class="brush: js">
[0, 1, 2, 5, 10].map(<strong>(x) =&gt; x * x * x</strong>); // вернет [0, 1, 8, 125, 1000].
</pre>

<p>Помимо упрощённого синтаксиса, такие функции всегда неявно привязываются в МОМЕНТ ОБЪЯВЛЕНИЯ к текущему лексическому<em>&nbsp;контексту</em>&nbsp;выполнения:</p>

<pre>
<code>function Person(){
  this.age = 0;

  setInterval(() =&gt; {
    this.age++; // в данном случае this будет ссылаться на создаваемый объект obj, а не на window
  }, 1000);
}

var obj = new Person();</code></pre>

<p id="Predefined_functions">Полное описание в статье справочника {{jsxref("Functions/Arrow_functions")}}</p>

<p id="Arrow_functions">Смотри также статью на&nbsp;hacks.mozilla.org: "<a href="https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/">ES6 In Depth: Arrow functions</a>".</p>

<h3 id="В_стиле_ООП">В стиле ООП</h3>

<p>Учитывая то, что функция по сути является объектом,&nbsp;можно&nbsp;использовать оператор <code>new&nbsp;</code>и&nbsp;<a href="/en-US/docs/JavaScript/Guide/Predefined_Core_Objects#Function_Object" title="en-US/docs/JavaScript/Guide/Predefined Core Objects#Function Object"><code>Function</code> конструктор</a> чтобы создавать функции динамически во время выполнения (подобно тому как это делает <code><a href="/en-US/docs/JavaScript/Guide/Functions#eval_Function" title="en-US/docs/JavaScript/Guide/Functions#eval_Function">eval()</a>).</code>&nbsp;</p>

<div class="warning">
<p>Однако такого подхода следует избегать из соображений производительности и безопасности.</p>
</div>

<pre class="brush: js">
var powerOfFive = new Function('x',
&nbsp;                 'return ' + Array(5).map(()=&gt;'x').join('*'));</pre>

<h4 id="Методы">Методы</h4>

<p>Функции очень часто используются как <em>методы</em>&nbsp;объектов, реализующих&nbsp;ООП.&nbsp;</p>

<dl>
 <dt>Метод&nbsp;</dt>
 <dd>это функция, заданная как значение&nbsp;свойства&nbsp;объекта.</dd>
</dl>

<p>Специальный синтаксис вызова методов позволяет неявно передавать объект в качестве <em>привязки(this)</em>.</p>

<pre class="brush: js">
class Greeting{

&nbsp; constructor(prefix){
&nbsp;   this.prefix = prefix;
&nbsp; }

  // это метод:
&nbsp; hello(name){
&nbsp;   return `${this.prefix}, ${name}`;
  }
}

var obj = new Greeting("Привет");

// вызов метода (obj передаётся в качестве <em>контекста `this`</em>)
obj.hello('Вася');

</pre>

<p>Больше информации об объектах и методах в <a href="/en-US/docs/JavaScript/Guide/Working_with_Objects" title="en-US/docs/JavaScript/Guide/Working with Objects">Работа с Объектами</a>.</p>

<h2 id="Вызов_функций"><a id="call" name="call"></a>Вызов функций</h2>

<ul>
</ul>

<p>Помимо манипуляций c ними как с обычными объектами, функции можно <em>вызывать&nbsp;-&nbsp;</em>запустить процесс&nbsp;вычисления&nbsp;выражений над данными</p>

<dl>
 <dt><strong>Вызов (call)</strong>&nbsp;функции</dt>
 <dd>последовательное выполнение управляющий инструкций и выражений&nbsp;из&nbsp;тела функции применительно к входным данным и контексту.&nbsp;</dd>
 <dt><strong>Выражение (expression)&nbsp;</strong></dt>
 <dd>комбинация математических и специальных операций, а также вызовов&nbsp;функций, которые описывают способ вычисления результирующего значения(result) в зависимости от входящих данных(input).&nbsp;</dd>
</dl>

<p>В момент вызова фунции могут&nbsp;быть переданы&nbsp;</p>

<ul>
 <li>входные данные в виде списка аргументов-значений,</li>
 <li>а также <strong>объект привязки</strong>&nbsp;- специальный аргумент, именуемый&nbsp;ключевым словом <code>this</code>.</li>
</ul>

<p>Кроме этого, выражения в функции могут адресоваться:</p>

<ul>
 <li><span style="line-height:1.5">к константам (строковые литералы, числа и т.д.)</span></li>
 <li>к локальным переменным,</li>
 <li>а также к внешним свободным переменным по цепочке&nbsp;<em>замыкания</em>.</li>
</ul>

<ul>
</ul>

<p>В результате выполнения функция</p>

<ul>
 <li>возвращает&nbsp;некоторое значение (явно с помощью&nbsp;<code>return</code>&nbsp;или неявно)&nbsp;</li>
 <li>или бросает&nbsp;исключение.</li>
</ul>

<p>Например, вызвать &nbsp;<code>square()</code>&nbsp;можно следующим&nbsp;образом:</p>

<pre class="brush: js">
// Функция выполняет свои инструкции над аргументом 5 
// и возвращает значение-результат 25

var result = square(5);

</pre>

<div class="note">
<p>Вызов функции должен&nbsp;быть выполнен&nbsp;в пределах её <em>области видимости</em> после того как функция объявлена.&nbsp;Область видимости функции&nbsp;определяется точно&nbsp;также как и для переменной любого другого типа.</p>
</div>

<h3 id="Рекурсия">Рекурсия</h3>

<dl>
 <dt>Рекурсивная функция&nbsp;</dt>
 <dd>функция, содержащая вызов самой&nbsp;себя непосредственно в&nbsp;своём теле либо через другие функции.</dd>
</dl>

<div class="syntaxbox">Классический пример рекурсивной функции вычисляющей факториал:</div>

<pre class="brush: js">
function factorial(n) {
  if ((n == 0) || (n == 1))
    return 1;
  else
    return (n * factorial(n - 1));
}

//вычислить факториал пяти:
var e = factorial(5); // <em>e</em> будет равно 120

</pre>

<h3 id="Вызов_с_помощью_.apply()_и_.call()">Вызов с помощью .apply() и .call()</h3>

<p>Существуют и другие способы вызывать функции. Поскольку функции сами являются объектами, они&nbsp;содержат собственные методы. В частности, метод <a href="/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply" title="apply"><code>apply()</code></a>, может использоваться, например,&nbsp;когда&nbsp;нужно адресоваться к функции динамически, или передать различное количество аргументов, или явно указать <em>контекст</em>.</p>

<pre class="brush: js">
var fn = resolveAction();
fn.apply(context, [number1, number2]);</pre>

<p>Смотрите<code> </code><a href="/en-US/docs/JavaScript/Guide/Obsolete_Pages/Predefined_Core_Objects/Function_Object" title="Function Object"><code>Function</code> объект</a>&nbsp;для более детального понимания.</p>

<h2 id="Параметры_и_аргументы_вызова">Параметры и аргументы вызова</h2>

<dl>
 <dt>Параметры функции</dt>
 <dd>список идентификаторов&nbsp;(и возможно сопоставленных им&nbsp;значений по умолчанию и&nbsp;типов данных), заданный В МОМЕНТ ОБЪЯВЛЕНИЯ.</dd>
 <dd>Параметры удобно рассматривать как объявления локальных переменных, каждая из которых инициализируются в МОМЕНТ ВЫЗОВА соответствующим&nbsp;значением из списка аргументов (или значением по умолчанию).</dd>
 <dt>Аргументы(входные данные) функции</dt>
 <dd>произвольный список значений, передаваемых функции В МОМЕНТ ВЫЗОВА.</dd>
</dl>

<div class="note">
<p>Соответствие имён параметров и значений аргументов задано <s>тупо</s> порядком их перечисления при обявлении/вызове.</p>
</div>

<div class="warning">
<p>Параметры в&nbsp;&nbsp;Javascript никак не ограничивают ни&nbsp;количество, ни содержание аргументов.</p>
</div>

<h3 id="sect1">&nbsp;</h3>

<h3 id="По_значению_или_по_ссылке">По значению или по ссылке</h3>

<p>Типы аргументов&nbsp;функции могут быть как примитивами (строки, числа, логические(boolean)), так и&nbsp;объектами (включая&nbsp;<a href="/en-US/docs/JavaScript/Reference/Global_Objects/Array" title="Array"><code>Array</code></a> или&nbsp;функции):</p>

<ul>
 <li>Значения-примитивы передаются в функции <strong>по значению</strong>: значение КОПИРУЕТСЯ, так что&nbsp;если функция изменит значение параметра, это изменение не будет иметь внешнего эффекта.</li>
 <li>объекты передаются в фунцию <strong>по ссылке:</strong>&nbsp; переприсваивание самой ссылки также не имеет внешнего эффекта, НО&nbsp;если функция изменяет свойства объекта, то эти изменения будут видимы вне функции (побочный эффект).</li>
</ul>

<p><u>пример:</u>&nbsp; Стрёмный побочный эффект от изменения свойств объекта-аргумента:</p>

<pre class="brush: js">
function myFunc(patient) {
  patient.gender= "F";
}

var he = {gender: "M"};

myFunc(he);

var y = he.gender;     // y gets the value "F", oops
</pre>

<p>Назначение параметру&nbsp;новой ссылки на объект&nbsp;не имеет влияния вне функции. Это продемонстрировано в примере ниже:</p>

<pre class="brush: js">
function myFunc(theObject) {
  theObject = {make: "Ford", model: "Focus", year: 2006};
}

var mycar = {make: "Honda", model: "Accord", year: 1998},
    x,
    y;

x = mycar.make;     // x gets the value "Honda"

myFunc(mycar);
y = mycar.make;     // y still gets the value "Honda" 
</pre>

<p>В первом случае, объект <code>mycar</code> был передан в функцию <code>myFunc</code>, которая изменила&nbsp;его состояние. Во втором случае, функция не изменяла переданный объект; вместо этого, она создавала новую локальную переменную с тем же именем что и имя параметра функции, так что никак не влияет на глобальный объект передаваемый в функцию.</p>

<h3 id="Использование_объекта_arguments">Использование объекта <code>arguments</code></h3>

<p>Доступ к аргументам может быть получен непосредственно (без обращения по именам объявленных параметров).</p>

<p>Ключевое слово&nbsp;<code>arguments</code>&nbsp;адресует структуру, где&nbsp;содержится список входных аргументов в порядке передачи при вызове.&nbsp;Общее количество аргументов содержится в <code>arguments.length, а значение отдельного&nbsp;аргумента можно получить как&nbsp;</code>arguments[i], где&nbsp;<code>i</code>&nbsp;- порядковый номер аргумента начиная с нуля.&nbsp;Таким образом, например, первый аргумент переданный в функцию будет <code>arguments[0]</code>. .</p>

<div class="note">
<p>Используя объект<code> arguments</code>,&nbsp;можно адресоваться к большему&nbsp;количеству&nbsp;аргументов, чем это описано в параметрах функции. Это часто полезно, если вы не знаете заранее как много аргументов будет передаваться в функцию.&nbsp;</p>
</div>

<div class="note">
<p>Заметьте, что структура&nbsp;<code>arguments</code>&nbsp;<s>крякает как утка</s>&nbsp;похожа на массив, но массивом не является.</p>
</div>

<p>Рассмотрим, например,&nbsp;функцию которая объединяет произвольное количество строк. Единственный <s>аргумент</s>&nbsp;параметр в определении функции - это строка-разделитель:</p>

<pre class="brush: js">
function myConcat(separator) {
   var result = ""; // initialize list
       
   // iterate through arguments
   for (var i = 1; i &lt; arguments.length; i++) {
      result += arguments[i] + separator;
   }
   return result;
}

// returns "red, orange, blue, " 
myConcat(", ", "red", "orange", "blue"); 

// returns "elephant; giraffe; lion; cheetah; " 
myConcat("; ", "elephant", "giraffe", "lion", "cheetah"); 

</pre>

<h3 id="Аргументы_и_оператор_развёртки">Аргументы и оператор развёртки</h3>

<p>Современный стандарт языка позволяет в элегантной функциональной манере&nbsp;отказаться от <code>arguments</code>, заменив его&nbsp;оператором развёртки(spread) (...)&nbsp;:</p>

<pre class="brush: js">
const myConcat = (sep, ...strings) =&gt; strings.join(sep) // !!!

</pre>

<h3 id="Значения_по_умолчанию">Значения по умолчанию</h3>

<p>В JavaScript, значения параметров по умолчанию устанавливаются в&nbsp;<code>undefined</code>.</p>

<p>В определённых ситуациях было бы удобно назначать другие значения по умолчанию. В прошлом<span style="line-height:1.5">, общий подход состоял в ручной проверке значения параметра на </span><code>undefined&nbsp;</code><span style="line-height:1.5">и в зависимости от результата, установки более подходящего значения по умолчанию:</span></p>

<pre>
<code>function multiply(a, b) {
  b = typeof b !== 'undefined' ?  b : 1;

  return a*b;
}

multiply(5); // 5</code></pre>

<p>Современный стандарт языка вводит прекрасную возможность явного указания значения по умолчанию с помощью выражения&nbsp;при ОБЪЯВЛЕНИИ функции:</p>

<pre>
<code>function multiply(a, b = 1) {
  return a*b;
}

multiply(5); // 5</code></pre>

<div class="note">
<p>Заметим что вычисление значения по умолчанию будет происходить В МОМЕНТ ВЫЗОВА функции&nbsp;</p>
</div>

<p>Также будет нелишним заметить что такие выражения&nbsp;могут адресоваться к предыдущим параметрам:</p>

<pre class="brush: js">
<code>function multiply(a, b = 2*a) { return a*b; } 

multiply(5); // 50</code></pre>

<p>Детальная информация в статье справочника&nbsp;<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters">default parameters</a>&nbsp;</p>

<h2 id="Область_данных_и_замыкания">Область данных и замыкания</h2>

<p>В момент вычисления выражений&nbsp;имена переменных необходимо подменить соответствующими&nbsp;значениями.</p>

<p>Для этих целей в JavaScript реализован &nbsp;механизм&nbsp;разрешения имён на основе&nbsp;<em>области&nbsp;данных(</em>Scope<em>) и</em>&nbsp;<em>замыканий(Closure)</em>:</p>

<pre class="brush: js">
<code>var prefix = "Привет";
function sayHi(name) {
  var phrase = prefix + ", " + name;
  alert( phrase );
}</code></pre>

<pre class="brush: js">
// логика разрешения переменных
RESOLVE = (env, id) =&gt; {
 if (env[id]) return env[id];
 if (env.OUTER_SCOPE) return RESOLVE(env.OUTER_SCOPE, id);
&nbsp;throw new Error('ReferenceError: variable %id is not declared')
}
...
// внешний область данных, актуальный на момент создания функции
SCOPE = { <code>prefix: "Привет"</code> , sayHi: undefined} 
...
// замыкание = функция + внешнее окружение
var sayHi = { 
&nbsp;OUTER_SCOPE: SCOPE, //захвачен в момент создания функции
&nbsp;PARAMETERS:['name'] 
 BODY: ()=&gt;{

&nbsp; // внутренняя область данных, создаваемая неявно в момент вызова
  SCOPE = { 
&nbsp;   name: 'Вася', 
    phrase: undefined,
&nbsp;   OUTER_SCOPE: sayHi.OUTER_SCOPE // образуем цепочку 
  }

  var phrase = 
&nbsp;            RESOLVE(SCOPE,'prefix') // будет извлечён из внешней области
&nbsp;           <code> + </code><code>", "</code>
&nbsp;            + RESOLVE(SCOPE,'name'); // будет извлечён из внутренней области

  alert( RESOLVE(SCOPE,'phrase' );

&nbsp;}
}

// передаётся контекст null и список аргументов
CALL_FUNCTION(sayHi, null, ['Вася']);</pre>

<p>1) Каждый раз В МОМЕНТ ВЫЗОВА функции,&nbsp;происходит неявное создание&nbsp;нового экземпляра&nbsp;<strong>области данных(</strong>Scope<strong>)&nbsp;-&nbsp;</strong>специального объекта&nbsp;, где ДО НАЧАЛА ВЫПОЛНЕНИЯ(<strong>hoisting</strong>) регистрируются</p>

<ul>
 <li>переменные из списка&nbsp;параметров функции, проинициализированные&nbsp;соответствующими аргументами-значениями.</li>
 <li>переменные, явно&nbsp;объявленные в теле функции с помощью <code>var</code></li>
 <li>вложенные функции, объявленные в теле в декларативном стиле</li>
 <li>неявная&nbsp;ссылка-замыкание[[OuterScope]]&nbsp;данной функции (образуя цепочку замыканий лексического диапазона)</li>
</ul>

<p>2) В МОМЕНТ ОБЪЯВЛЕНИЯ объект-функция неявно получает постоянную ссылку&nbsp;[[OuterScope]] на&nbsp;актуальную&nbsp;<em>область переменных</em>&nbsp; выполняющейся&nbsp;функции-контейнера<em>. </em></p>

<p>Эта ссылка (или в более общем смысле - связка функция + ссылка) называется <strong>замыканием</strong>. Она позволяет адресоваться из тела функции&nbsp; к внешним(свободным) переменным.</p>

<p>3) Если искомое имя переменной не обнаружено в собственной <em>области переменных</em>, то поиск продолжается последовательно вдоль&nbsp;по <em>цепочке замыканий(scope chain)</em>&nbsp;пока имя не будет найдено (в противном случае сработает <em>исключение &nbsp;<code>ReferenceError</code></em>).</p>

<div class="note">
<p>В спецификации ECMA-262, &nbsp;присутствует гораздо больше деталей, в частности речь идёт о двух объектах:&nbsp;<code>VariableEnvironment</code>&nbsp;и&nbsp;<code>LexicalEnvironment</code>. Но<span style="line-height:1.5">&nbsp;мы абстрагируемся и используем везде термин&nbsp;</span><code style="font-style: normal; font-weight: normal; line-height: 1.5;">Scope</code><span style="line-height:1.5">, это&nbsp;позволяет <u>достаточно</u> точно описать происходящее.</span></p>

<p>Более формальное описание находится в спецификации ECMA-262, секции 10.2-10.5 и 13.</p>
</div>

<p>Таким образом,&nbsp;общее правило видимости и доступности переменных таково</p>

<blockquote>
<p>&nbsp;Локальные переменные и параметры функции,&nbsp;видны ТОЛЬКО в пределах этой функции и&nbsp;внутри&nbsp;всех вложенных функций на всю глубину.</p>

<p>Они доступны на протяжении всего времени существования функций, которые к ним обращаются.</p>
</blockquote>

<div class="note">
<p>Замыкание продожает существовать пока существует сама функция (несмотря на то, что внешняя функция возможно уже завершила выполнение и снесена сборщиком мусора).</p>

<p>Таким образом, время жизни локальных&nbsp;переменных функции определяется временем выполнения/использования этой функции и всех вложенных функций.</p>
</div>

<div class="note">
<p>Современный стандарт языка позволяет определять локальные переменые в рамках отдельно взятого&nbsp;блока кода с помощью <code>let </code>и&nbsp;<code>const</code>. В этом случае, все правила продолжают работать как если бы каждый блок кода был неявной вложенной функцией.</p>
</div>

<p><u>пример:</u>&nbsp; Вложенная&nbsp;функция&nbsp;<code>incrementor&nbsp;</code>обращается к параметру <code>base</code> внешней&nbsp;функции <code>factory</code> (замыкание продолжает существовать после завершения внешней функции):</p>

<pre class="brush: js">
function factory (base) {
  
  // returning function has access to external `base` parameter  
  return function incrementor(inc) { return base + inc; };
}

var sum2 = factory(2);

sum2(5); // Returns 7
</pre>

<p id="Замыкания"><u>пример:</u> &nbsp;это может быть немного сложнее, но принцип тот же - внутри вложенных функций секс доступен во всех направлениях - на запись и на чтение)).</p>

<pre class="brush: js">
var createPet = function(name) {
  var sex;
  
  return {
    setName: function(newName) {
      name = newName;
    },
    
    getName: function() {
      return name;
    },
    
    getSex: function() {
      return sex;
    },
    
    setSex: function(newSex) {
      if(typeof newSex == "string" &amp;&amp; (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie"); 
pet.setSex("female"); 
pet.getName(); // Vivie
pet.getSex(); // female 

var pet2 = createPet("Oliver"); 
pet2.setSex("male"); 
pet2.getName(); // Oliver
pet2.getSex();  // male
                  
</pre>

<p>Переменные внутренней функции могут использоваться&nbsp;в качестве безопасных хранилищ для закрытых(private) данных:</p>

<pre class="brush: js">
var getCode = (function(){
  // никто не попробует сахарку
  var secureCode = "сахарок";
​ 
  return function (token) {
    return mix(secureCode, token);
  };
})();

getCode();    // Returns the secret code
</pre>

<div class="warning">
<p>Если замкнутая функция определяет переменную с тем же именем что и переменная во внешней области видимости, то становится невозможным более получить ссылку на переменную во внешней области видимости.</p>
</div>

<p>Пример со перекрытием имён переменных&nbsp;внешней и внутренней функций</p>

<pre class="brush: js">
var createPet = function(name) {  // Outer function defines a variable called "name"
  return {
    setName: function(name) {    // Enclosed function also defines a variable called "name"
      name = name;               // ??? How do we access the "name" defined by the outer function ???
    }
  }
}
</pre>

<h2 id="Объект_привязки_(this)">Объект привязки (this)</h2>

<dl>
 <dt><strong>объект привязки</strong>&nbsp;</dt>
 <dd>специальный аргумент, именуемый&nbsp;ключевым словом <code>this</code>.</dd>
</dl>

<p>Основное назначение объекта привязки состоит в поддержке вызовов методов объектов в стиле ООП (см. выше).</p>

<pre class="brush: js">
// для методов объекта 
obj.method(param); // -&gt;  obj.method.call(obj, param)

// или для внешних функций:
var obj = {}, fn = function(){};
obj::fn(param) // -&gt; fn.call(obj, param)</pre>

<p class="brush: js">Ссылка на объект привязки&nbsp;<code>this </code>передаётся</p>

<ul>
 <li>для обычных функций -&nbsp;каждый раз В МОМЕНТ ВЫЗОВА.</li>
 <li>для стрелочных&nbsp;функций - связывается в МОМЕНТ ОБЪЯВЛЕНИЯ</li>
</ul>

<p>Стандарт языка предоставляет средства явного связывания&nbsp;</p>

<pre class="brush: js">
var obj = { some : function (){...} }

var boundSome = some.bind(obj);

// или ещё проще:

var boundSome2 = ::obj.some;</pre>

<h2 id="Далее">Далее</h2>

<p>Подробное техническое описание функций в статье справочника {{jsxref("Functions","Функции")}}</p>

<p>Смотрите также&nbsp;<code><a href="/en-US/docs/JavaScript/Reference/Global_Objects/Function" title="en-US/docs/JavaScript/Reference/Global Objects/Function">Function</a>&nbsp;</code>в&nbsp;Справочнике JavaScript для получения дополнительной информации по функции как объекту.</p>

<p>Внешние ресурсы:</p>

<ul>
 <li><a href="https://www.ecma-international.org/ecma-262/6.0/index.html#sec-ecmascript-function-objects">ECMAScript® 2015 Language Specification</a></li>
 <li><a href="https://learn.javascript.ru/closures">Учебник по Javascript - замыкания</a></li>
</ul>

<p>{{ autoPreviousNext("JSGChapters") }}</p>
Вернуть эту версию