Функция стрелки против объявления / выражения функции: являются ли они эквивалентными / заменяемыми?
Канонический вопрос Если после замены объявления / выражения функции на функцию стрелки вы обнаружите вопрос о проблемах, закройте его как дубликат этого.
Функции стрелок в ES2015 обеспечивают более краткий синтаксис. Могу ли я заменить все мои объявления / выражения функций функциями стрелок сейчас? Что я должен высматривать?
Примеры:
Функция конструктора
function User(name) {
this.name = name;
}
// vs
const User = name => {
this.name = name;
};
Методы прототипа
User.prototype.getName = function() {
return this.name;
};
// vs
User.prototype.getName = () => this.name;
Объектные (буквальные) методы
const obj = {
getName: function() {
// ...
}
};
// vs
const obj = {
getName: () => {
// ...
}
};
Callbacks
setTimeout(function() {
// ...
}, 500);
// vs
setTimeout(() => {
// ...
}, 500);
Вариадические функции
function sum() {
let args = [].slice.call(arguments);
// ...
}
// vs
const sum = (...args) => {
// ...
};
6 ответов
ДЛ: Нет! Функции стрелок и объявления / выражения функций не эквивалентны и не могут быть заменены вслепую.
Если функция, которую вы хотите заменить, не использует this
, arguments
и не вызывается с new
, тогда да.
Как часто: это зависит. Функции со стрелками ведут себя иначе, чем объявления / выражения функций, поэтому давайте сначала рассмотрим различия:
1. Лексический this
а также arguments
Функции стрелок не имеют своих собственных this
или же arguments
связывание. Вместо этого эти идентификаторы разрешаются в лексической области, как и любая другая переменная. Это означает, что внутри функции стрелки, this
а также arguments
обратитесь к значениям this
а также arguments
в среде функция стрелки определена в (т. е. "вне" функции стрелки):
// Example using a function expression
function createObject() {
console.log('Inside `createObject`:', this.foo);
return {
foo: 42,
bar: function() {
console.log('Inside `bar`:', this.foo);
},
};
}
createObject.call({foo: 21}).bar(); // override `this` inside createObject
// Example using a arrow function
function createObject() {
console.log('Inside `createObject`:', this.foo);
return {
foo: 42,
bar: () => console.log('Inside `bar`:', this.foo),
};
}
createObject.call({foo: 21}).bar(); // override `this` inside createObject
В случае выражения функции, this
относится к объекту, который был создан внутри createObject
, В случае функции стрелки this
относится к this
из createObject
сам.
Это делает функции стрелок полезными, если вам нужен доступ к this
текущей среды:
// currently common pattern
var that = this;
getData(function(data) {
that.data = data;
});
// better alternative with arrow functions
getData(data => {
this.data = data;
});
Обратите внимание, что это также означает, что невозможно установить функцию стрелки this
с .bind
или же .call
,
Если вы не очень знакомы с this
рассмотреть вопрос о прочтении
2. Функции стрелок не могут быть вызваны с new
ES2015 различает функции, которые могут вызываться, и функции, которые могут быть созданы. Если функция конструируема, ее можно вызвать с new
т.е. new User()
, Если функция вызывается, она может быть вызвана без new
(т.е. нормальный вызов функции).
Функции, созданные с помощью объявлений / выражений функций, могут быть как конструируемыми, так и вызываемыми.
Функции стрелок (и методы) могут быть вызваны только. class
конструкторы только конструктивны.
Если вы пытаетесь вызвать не вызываемую функцию или создать не конструируемую функцию, вы получите ошибку во время выполнения.
Зная это, мы можем утверждать следующее.
Сменная:
- Функции, которые не используют
this
или жеarguments
, - Функции, которые используются с
.bind(this)
Не заменимо:
- Функции конструктора
- Функция / методы, добавленные в прототип (потому что они обычно используют
this
) - Variadic функции (если они используют
arguments
(увидеть ниже))
Давайте рассмотрим это подробнее на ваших примерах:
Функция конструктора
Это не будет работать, потому что функции стрелок не могут быть вызваны с new
, Продолжайте использовать объявление функции / выражение или использование class
,
Методы прототипа
Скорее всего нет, потому что методы-прототипы обычно используют this
чтобы получить доступ к экземпляру. Если они не используют this
Тогда вы можете заменить его. Однако, если вы в первую очередь заботитесь о лаконичном синтаксисе, используйте class
с кратким синтаксисом метода:
class User {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
Методы объекта
Аналогично для методов в литерале объекта. Если метод хочет обратиться к самому объекту через this
, продолжайте использовать выражения функций или используйте синтаксис нового метода:
const obj = {
getName() {
// ...
},
};
Callbacks
Это зависит. Вы должны определенно заменить его, если вы используете псевдонимы внешнего this
или используете .bind(this)
:
// old
setTimeout(function() {
// ...
}.bind(this), 500);
// new
setTimeout(() => {
// ...
}, 500);
Но: если код, который вызывает обратный вызов, явно устанавливает this
к определенному значению, как это часто бывает с обработчиками событий, особенно с jQuery, и обратный вызов использует this
(или же arguments
), вы не можете использовать функцию стрелки!
Вариадические функции
Так как функции стрелок не имеют своих собственных arguments
Вы не можете просто заменить их функцией стрелки. Тем не менее, ES2015 представляет альтернативу использованию arguments
: параметр отдыха.
// old
function sum() {
let args = [].slice.call(arguments);
// ...
}
// new
const sum = (...args) => {
// ...
};
Связанный вопрос:
- Когда я должен использовать функции Arrow в ECMAScript 6?
- Есть ли у стрелочных функций ES6 свои аргументы или нет?
- Каковы различия (если таковые имеются) между функциями стрелок ES6 и функциями, связанными с Function.prototype.bind?
- Как использовать стрелку ES6 в методах класса?
Другие ресурсы:
Стрелочные функции => лучшая функция ES6 на данный момент. Это чрезвычайно мощное дополнение к ES6, которое я использую постоянно.
Подождите, вы не можете использовать стрелочную функцию везде в своем коде, она не будет работать во всех случаях, например this
где стрелочные функции не используются. Без сомнения, стрелочная функция - отличное дополнение, она упрощает код.
Но вы не можете использовать стрелочную функцию, когда требуется динамический контекст: определение методов, создание объектов с конструкторами, получение цели при обработке событий.
НЕЛЬЗЯ использовать стрелочные функции, потому что:
У них нет
this
Он использует "лексическую область видимости", чтобы выяснить, каково значение "
this
â € должно быть. В простом словесном лексическом охвате он использует â € œthis
â € изнутри тела функции.У них нет
arguments
Стрелочные функции не имеют
arguments
объект. Но такой же функциональности можно добиться с помощью остальных параметров.let sum = (...args) => args.reduce((x, y) => x + y, 0)
sum(3, 3, 1) // output - 7
`Их нельзя использовать с
new
Стрелочные функции не могут быть конструкторами, потому что у них нет свойства прототипа.
Когда использовать стрелку, а когда нет:
- Не используйте для добавления функции как свойства в литерал объекта, потому что мы не можем получить к нему доступ.
- Функциональные выражения лучше всего подходят для объектных методов. Стрелочные функции лучше всего подходят для обратных вызовов или таких методов, как
map
,reduce
, илиforEach
. - Используйте объявления функций для функций, которые вы вызываете по имени (потому что они были подняты).
- Используйте стрелочные функции для обратных вызовов (потому что они, как правило, короче).
Они не всегда эквивалентны. Вот случай, когда вы не можете просто использовать стрелочные функции вместо обычных функций.
Стрелочные функции НЕ МОГУТ использоваться в качестве конструкторов.
TLDR:
Это связано с тем, как функции со стрелками используют ключевое слово this . JS просто выдаст ошибку, если увидит, что функция стрелки вызывается как «конструктор». Используйте обычные функции, чтобы исправить ошибку.
Более длинное объяснение:
Это связано с тем, что "конструкторы" объектов полагаются на ключевое слово this для возможности изменения.
Как правило, ключевое слово this всегда ссылается на глобальный объект. (В браузере это объект окна ).
НО, когда вы делаете что-то вроде:
function personCreator(name) {
this.name = name;
}
const person1 = new personCreator('John');
Ключевое слово new выполняет часть своей магии и делает ключевое слово this внутри personCreator изначально пустым объектом, а не ссылкой на глобальный объект. После этого внутри этого пустого объекта создается новое свойство с именем name , и его значением будет «John». В конце возвращается этот объект.
Как мы видим, ключевое слово new изменило значение this со ссылки на глобальный объект на пустой объект {} .
Стрелочные функции не позволяют изменять свой объект this . Их объект this всегда является объектом из той области, в которой они были статически созданы. Это называется статической лексической областью . Вот почему вы не можете выполнять такие операции, как bind , apply или call со стрелочными функциями. Просто их this привязано к значению this области, в которой они были созданы. Это по дизайну.
И из-за этого :D стрелочные функции нельзя использовать как "конструкторы".
Боковое примечание :
Лексическая область видимости — это просто область, в которой создается функция . Например:
function personCreator(name) {
this.name = name;
const foo = () => {
const bar = () => {
console.log(this); // Output: { name: 'John' }
}
console.log(this); // Output: { name: 'John' }
bar();
}
foo();
}
const person1 = new personCreator('John');
Лексическая область действия bar — это все, что находится внутри foo . Таким образом, это значение bar равно тому, что есть у foo , то есть у personCreator .
Чтобы использовать стрелочные функции с function.prototype.call
, Я создал вспомогательную функцию для прототипа объекта:
// Using
// @func = function() {use this here} or This => {use This here}
using(func) {
return func.call(this, this);
}
Применение
var obj = {f:3, a:2}
.using(This => This.f + This.a) // 5
редактировать
Вам не НУЖЕН помощник. Вы могли сделать:
var obj = {f:3, a:2}
(This => This.f + This.a).call(undefined, obj); // 5
Посмотрите на этот пример Plnkr
Переменная this
очень разные timesCalled
увеличивается только на 1 каждый раз, когда кнопка вызывается. Который отвечает на мой личный вопрос:
.click( () => { } )
а также
.click(function() { })
оба создают одинаковое количество функций при использовании в цикле, как вы можете видеть из подсчета Guid в Plnkr.
Функции стрелок JavaScript - это просто еще один способ определения функции.
Функции со стрелками не только делают ваш код более понятным, конкретным и легким для чтения. Это также обеспечивает преимущества неявного возврата.
Ниже я поделюсь некоторыми простыми примерами, которые сами объясняют объявление функции и как определять функции стрелок JavaScript.
/*----------------------------------
JavaScript Arrow Functions
----------------------------------*/
// Defining a function.
function addNumbers(a, b) {
return a + b;
}
addNumbers(10, 6); // 16
// Using anonymous function.
var addNumbers = function(a, b) {
return a + b;
}
addNumbers(10, 6); // 16
// using Arrow Functions or Fat Arrow functions.
var addNumbers = (a, b) => {
return a + b; // with return statement
}
addNumbers(10, 6); // 16
// Using Arrow Functions or Fat Arrow functions without return statements and without curly braces.
var addNumbers = (a, b) => a + b; // this is a condensed way to define a function.
addNumbers(10, 6); // 16