Ось перелік стандартних форм, що створюють функції: (Спочатку написано для іншого питання, але адаптовано після переходу в канонічне запитання.)
Умови:
Швидкий список:
Декларація функції
"Анонімний" 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
у специфікації - вона з’являється в усьому місці.)
Іменований 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
вище приклад - одна з них. Якщо у вас є кілька аргументів для передачі функції, ви використовуєте паролі:
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));
Версія без { ... }
називається функцією стрілки з виразом тіла або стислого тіла . (Також: Коротка функція стрілки.) Функція, що { ... }
визначає тіло, - це функція стрілки з тілом функції . (Також: Докладна функція стрілки.)
Декларація методу в ініціалізаторі об'єктів (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
.