Вот краткое изложение стандартных форм, которые создают функции: (Первоначально написано для другого вопроса, но адаптировано после перемещения в канонический вопрос.)
Условия:
Быстрый список:
Объявление функций
«Аноним» function
Выражение (которое, несмотря на термин, иногда создает функции с именами)
По имени function
Выражение
Инициализатор функций доступа (ES5 +)
Выражение функции стрелки (ES2015 +) (которое, как и выражения анонимных функций, не содержит явного имени и может создавать функции с именами)
Объявление метода в инициализаторе объекта (ES2015 +)
Объявления конструктора и метода в class
(ES2015 +)
Объявление функций
Первая форма - это объявление функции , которое выглядит так:
function x() {
console.log('x');
}
Объявление функции - это объявление ; это не утверждение или выражение. Таким образом, вы не следуете с ;
(хотя это безвредно).
Объявление функции обрабатывается, когда выполнение входит в контекст, в котором оно появляется, до выполняется любой пошаговый код. Создаваемой ей функции присваивается собственное имя (x
в приведенном выше примере), и это имя помещается в область, в которой появляется объявление.
Поскольку он обрабатывается перед любым пошаговым кодом в том же контексте, вы можете сделать что-то вроде этого:
x(); // Works even though it's above the declaration
function x() {
console.log('x');
}
До ES2015 спецификация не охватывала то, что должен делать движок JavaScript, если вы поместили объявление функции внутри структуры управления, такой как try
, if
, switch
, while
и т. Д., Как это :
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
И поскольку они обрабатываются до запуска пошагового кода , сложно знать, что делать, когда они находятся в управляющей структуре.
Несмотря на то, что до ES2015 до этого не было указано , это было допустимое расширение для поддержки объявлений функций в блоках. К сожалению (и неизбежно), разные движки делали разные вещи.
Начиная с ES2015, в спецификации сказано, что делать. Фактически, это дает три отдельных действия:
- Если в свободном режиме не в веб-браузере, движок JavaScript должен делать одну вещь
- Если в свободном режиме в веб-браузере движок JavaScript должен делать что-то еще
- Если в режиме строгий (браузер или нет), движок JavaScript должен делать еще одну вещь
Правила для свободных режимов сложны, но в строгом режиме объявления функций в блоках просты: они локальны для блока (они имеют область действия блока , что также является новым в ES2015), и они подняты к вершине блока. Итак:
"use strict";
if (someCondition) {
foo(); // Works just fine
function foo() {
}
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
// because it's not in the same block)
"Аноним" function
Выражение
Вторая общая форма называется выражением анонимной функции :
var y = function () {
console.log('y');
};
Как и все выражения, оно оценивается, когда достигается при пошаговом выполнении кода.
В ES5 создаваемая функция не имеет имени (она анонимна). В ES2015, функции по возможности присваивается имя, выводя его из контекста. В приведенном выше примере имя будет y
. Нечто подобное происходит, когда функция является значением инициализатора свойства. (Подробную информацию о том, когда это происходит и правилах, ищите SetFunctionName
в спецификации & mdash; она появляется во всем месте.)
Именовано function
Выражение
Третья форма - это именованное выражение функции ("NFE"):
var z = function w() {
console.log('zw')
};
Функция, которую она создает, имеет правильное имя (w
в данном случае). Как и все выражения, это оценивается, когда оно достигается при пошаговом выполнении кода. Имя функции не добавлено в область, в которой появляется выражение; имя находится в области действия * в самой функции:
var z = function w() {
console.log(typeof w); // "function"
};
console.log(typeof w); // "undefined"
Обратите внимание, что NFE часто являются источником ошибок для реализаций JavaScript. Например, IE8 и более ранние версии обрабатывают NFE совершенно неправильно , создавая две разные функции в два разных момента времени. Ранние версии Safari также имели проблемы. Хорошей новостью является то, что в текущих версиях браузеров (IE9 и выше, текущий Safari) таких проблем больше нет. (Но, к сожалению, на момент написания статьи IE8 все еще широко используется, и поэтому использование NFE с кодом для Интернета в целом все еще проблематично.)
Инициализатор функций доступа (ES5 +)
Иногда функции могут проникнуть в основном незамеченными; это относится к функциям доступа . Вот пример:
var obj = {
value: 0,
get f() {
return this.value;
},
set f(v) {
this.value = v;
}
};
console.log(obj.f); // 0
console.log(typeof obj.f); // "number"
Обратите внимание, что когда я использовал эту функцию, я не использовал ()
! Это потому, что это функция доступа для свойства. Мы получаем и устанавливаем свойство обычным способом, но за кулисами вызывается функция.
Вы также можете создавать функции доступа с помощью Object.defineProperty
, Object.defineProperties
и менее известным вторым аргументом Object.create
.
Выражение функции стрелки (ES2015 +)
ES2015 приносит нам функцию стрелки . Вот один пример:
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6
Видите, что n => n * 2
вещь скрывается в map()
звонке? Это функция.
Несколько вещей о функциях стрелок:
У них нет своих this
. Вместо этого они закрывают this
контекста, в котором они определены. (Они также закрываются над arguments
и, при необходимости, super
.) Это означает, что this
внутри них совпадает с this
там, где они созданы, и не может быть изменено.
Как вы уже заметили, вы не используете ключевое слово function
; вместо этого вы используете =>
.
Пример n => n * 2
, приведенный выше, является одной из их форм. Если у вас есть несколько аргументов для передачи функции, вы используете parens:
var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6
(Помните, что Array#map
передает запись в качестве первого аргумента, а индекс - в качестве второго.)
В обоих случаях тело функции является просто выражением; возвращаемое значение функции автоматически будет результатом этого выражения (вы не используете явное return
).
Если вы делаете больше, чем просто одно выражение, используйте {}
и явное return
(если вам нужно вернуть значение), как обычно:
var a = [
{first: "Joe", last: "Bloggs"},
{first: "Albert", last: "Bloggs"},
{first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
var rv = a.last.localeCompare(b.last);
if (rv === 0) {
rv = a.first.localeCompare(b.first);
}
return rv;
});
console.log(JSON.stringify(a));
Версия без { ... }
называется функцией стрелки с выражением тела или кратким текстом . (Также: A краткая функция стрелки.) Элемент с { ... }
, определяющим тело, является функцией стрелки с функцией тела . (Также: A подробная функция стрелки.)
Объявление метода в инициализаторе объекта (ES2015 +)
ES2015 допускает более короткую форму объявления свойства, которое ссылается на функцию, называемую Определение метода ; это выглядит так:
var o = {
foo() {
}
};
почти эквивалент в ES5 и более ранних версиях:
var o = {
foo: function foo() {
}
};
Разница (кроме многословия) в том, что метод может использовать super
, а функция - нет. Так, например, если бы у вас был объект, который определил (скажем) valueOf
с использованием синтаксиса метода, он мог бы использовать super.valueOf()
, чтобы получить значение, которое Object.prototype.valueOf
вернуло бы (прежде чем предположительно сделать что-то еще с ним), тогда как ES5 Версия должна будет сделать Object.prototype.valueOf.call(this)
вместо.
Это также означает, что метод имеет ссылку на объект, для которого он был определен, поэтому, если этот объект является временным (например, вы передаете его в Object.assign
как один из исходных объектов), синтаксис метода может означать, что объект сохраняется в памяти, если в противном случае он мог бы быть собран сборщиком мусора (если механизм JavaScript не обнаруживает эту ситуацию и не обрабатывает ее, если ни один из методов не использует super
).
Объявления конструктора и метода в class
(ES2015 +)
ES2015 предоставляет нам синтаксис class
, включая объявленные конструкторы и методы:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + " " + this.lastName;
}
}
Выше приведены два объявления функций: одно для конструктора, который получает имя Person
, и одно для getFullName
, которое является функцией, назначенной Person.prototype
.