Чи "Елементи стрілки" та "Функції" еквівалентні / обмінні?


520

Функції стрілок в 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) => {
  // ...
};

5
Подібні запитання щодо функцій стрілок все частіше виникають із тим, що ES2015 стає все популярнішим. Я не відчував, що є хороший канонічний питання / відповідь на це питання, тому я створив це. Якщо ви вважаєте, що вже є хороший, будь ласка, повідомте мене, і я закрию цю, як дублікат або видалити її. Сміливо вдосконалюйте приклади або додайте нові.
Фелікс Клінг

2
Що з JavaScript ecma6 змінити нормальну функцію на функцію стрілки ? Звичайно, звичайне питання ніколи не може бути таким хорошим і загальним, як те, що спеціально написано, щоб бути канонічним.
Бергі

Подивіться на цей приклад Plnkr Змінна thisмає дуже різний timesCalledприріст лише на 1 при кожному натисканні кнопки. Що відповідає на моє особисте запитання: .click( () => { } )і .click(function() { }) обидва створюють однакову кількість функцій при використанні в циклі, як ви бачите з підрахунку Guid в Plnkr.
abbaf33f

Відповіді:


750

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: параметра відпочинку .

// old
function sum() {
  let args = [].slice.call(arguments);
  // ...
}

// new
const sum = (...args) => {
  // ...
};

Питання, пов'язані з цим:

Подальші ресурси:


6
Можливо, варто згадати, що лексика thisтакож впливає superі що їх немає .prototype.
loganfsmyth

1
Було б також непогано згадати, що вони не є синтаксично взаємозамінними - функція стрілки ( AssignmentExpression) не може бути просто введена скрізь, де вираз функції ( PrimaryExpression) може, і вона досить часто відганяє людей (особливо, тому що вони розбирали помилки в основних реалізаціях JS).
JMM

@JMM: "Це досить часто їздить до людей", чи можете ви надати конкретний приклад? Позіхаючи за специфікацією, здається, що місця, де можна поставити ЗП, але не AF, все одно призведе до помилок виконання ...
Фелікс Клінг

Звичайно, я маю на увазі такі речі, як намагання негайно викликати функцію стрілки, як функцію вираз ( () => {}()) або робити щось подібне x || () => {}. Ось що я маю на увазі: помилки під час виконання (розбору). (І хоча це так, досить часто люди думають, що помилка є помилкою.) Ви просто намагаєтесь прикрити логічні помилки, які залишаться непоміченими, оскільки вони не обов'язково помиляються під час розбору чи виконання? new'є одна помилка виконання?
JMM

Ось декілька посилань на нього, що з'являються в дикій природі: substack / node-broserify # 1499 , babel / babel-eslint # 245 (це стрілка асинхронізації, але я думаю, що це те саме основне питання), і купа питань про Вавілону, яку зараз важко знайти, але ось один T2847 .
JMM

11

Функції стрілки => найкраща функція 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
2. У них немає аргументів, вибачте, це неправда, можна аргументувати без використання оператора ... можливо, ви хочете сказати, що у вас немає масиву як аргумент
Carmine Tambascia

@CarmineTambascia Прочитайте про спеціальний argumentsоб’єкт, який недоступний у функціях стрілок тут: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
vichle

0

Щоб використовувати функції зі стрілками 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.