Эквивалентны / взаимозаменяемы ли «стрелочные функции» и «функции»?

Dec 19 2015

Стрелочные функции в 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) => {
  // ...
};

Ответы

817 FelixKling Dec 19 2015 at 00:58

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) => {
  // ...
};

Связанный вопрос:

Дополнительные ресурсы:

17 Ashutosh Jan 10 2020 at 03:22

Стрелочные функции => лучшая функция ES6 на данный момент. Это чрезвычайно мощное дополнение к ES6, которое я использую постоянно.

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

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

НЕЛЬЗЯ использовать стрелочные функции, потому что:

  1. У них нет this

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

  2. У них нет arguments

    Стрелочные функции не имеют argumentsобъекта. Но той же функциональности можно добиться с помощью остальных параметров.

    let sum = (...args) => args.reduce((x, y) => x + y, 0) sum(3, 3, 1) // output - 7 `

  3. Их нельзя использовать с new

    Стрелочные функции не могут быть конструкторами, потому что у них нет свойства прототипа.

Когда использовать стрелочную функцию, а когда нет:

  1. Не используйте для добавления функции как свойства в литерал объекта, потому что мы не можем получить к нему доступ.
  2. Функциональные выражения лучше всего подходят для объектных методов. Стрелка функции лучше всего подходят для обратных вызовов или методов , таких как map, reduceили forEach.
  3. Используйте объявления функций для функций, которые вы вызываете по имени (потому что они подняты).
  4. Используйте стрелочные функции для обратных вызовов (потому что они, как правило, короче).
2 toddmo May 16 2020 at 08:56

Чтобы использовать стрелочные функции с 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