Эквивалентны / взаимозаменяемы ли «стрелочные функции» и «функции»?
Стрелочные функции в 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: () => {
// ...
}
};
Обратные вызовы
setTimeout(function() {
// ...
}, 500);
// vs
setTimeout(() => {
// ...
}, 500);
Вариадические функции
function sum() {
let args = [].slice.call(arguments);
// ...
}
// vs
const sum = (...args) => {
// ...
};
Ответы
tl; dr: Нет! Стрелочные функции и объявления / выражения функций не эквивалентны и не могут быть заменены вслепую.
Если функция , которую вы хотите заменить это не использовать 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 различает между функциями, которые называют в состоянии и функции, которые сооружать состоянии. Если функция constructable, она может быть вызвана с new
, то есть new User()
. Если функция вызываемая, она может быть вызвана без new
(т. Е. Обычного вызова функции).
Функции, созданные с помощью объявлений / выражений функций, можно создавать и вызывать.
Стрелочные функции (и методы) можно только вызывать.
class
конструкторы можно только построить.
Если вы пытаетесь вызвать невызываемую функцию или создать неконструктивную функцию, вы получите ошибку времени выполнения.
Зная это, можно констатировать следующее.
Заменяемый:
- Функции, которые не используют
this
илиarguments
. - Функции, которые используются с
.bind(this)
Не заменяется:
- Функции конструктора
- Функция / методы, добавленные в прототип (потому что они обычно используют
this
) - Вариативные функции (если они используются
arguments
(см. Ниже))
Давайте подробнее рассмотрим это на ваших примерах:
Конструктор функция
Это не сработает, потому что функции стрелок нельзя вызывать с помощью new
. Продолжайте использовать объявление / выражение функции или используйте class
.
Методы прототипа
Скорее всего, нет, потому что методы прототипа обычно используются this
для доступа к экземпляру. Если они не используются this
, то можно заменить. Однако, если вы в первую очередь заботитесь о кратком синтаксисе, используйте class
его краткий синтаксис метода:
class User {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
Методы объекта
Аналогично для методов в литерале объекта. Если метод хочет ссылаться на сам объект через this
, продолжайте использовать выражения функций или используйте новый синтаксис метода:
const obj = {
getName() {
// ...
},
};
Обратные вызовы
Это зависит. Вам обязательно следует заменить его, если вы используете псевдоним внешнего this
или используете .bind(this)
:
// old
setTimeout(function() {
// ...
}.bind(this), 500);
// new
setTimeout(() => {
// ...
}, 500);
Но: если код, который вызывает обратный вызов, явно устанавливает this
определенное значение, как это часто бывает с обработчиками событий, особенно с jQuery, и обратный вызов использует this
(или arguments
), вы не можете использовать функцию стрелки!
Вариадические функции
Поскольку у стрелочных функций нет своих собственных arguments
, вы не можете просто заменить их стрелочной функцией. Однако ES2015 предлагает альтернативу использованию arguments
: параметр rest .
// old
function sum() {
let args = [].slice.call(arguments);
// ...
}
// new
const sum = (...args) => {
// ...
};
Связанный вопрос:
- Когда мне следует использовать стрелочные функции в ECMAScript 6?
- У стрелочных функций ES6 есть свои аргументы или нет?
- Каковы различия (если есть) между стрелочными функциями ES6 и функциями, связанными с Function.prototype.bind?
- Как использовать стрелочные функции (поля открытого класса) в качестве методов класса?
Дополнительные ресурсы:
Стрелочные функции => лучшая функция 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
. - Используйте объявления функций для функций, которые вы вызываете по имени (потому что они подняты).
- Используйте стрелочные функции для обратных вызовов (потому что они, как правило, короче).
Чтобы использовать стрелочные функции с 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