Згідно з пропозицією , стрілки спрямовані на "вирішення та усунення декількох загальних больових точок традиційних Function Expression". Вони мали намір покращити питання, thisлексично зв’язуючи та пропонуючи короткий синтаксис.
Однак,
- Не можна послідовно пов'язувати
thisлексично
- Синтаксис функції стрілки тонкий і неоднозначний
Тому функції стрілок створюють можливості для плутанини та помилок, і їх слід виключати зі словника програміста JavaScript, замінювати functionвиключно.
Щодо лексичного this
this проблематично:
function Book(settings) {
this.settings = settings;
this.pages = this.createPages();
}
Book.prototype.render = function () {
this.pages.forEach(function (page) {
page.draw(this.settings);
}, this);
};
Функції стрілки мають намір вирішити проблему, коли нам потрібно отримати доступ до властивості thisвсередині зворотного дзвінка. Існує вже кілька способів зробити це: можна призначити thisзмінну, використовувати bindабо використовувати третій аргумент, доступний для Arrayметодів сукупності. Однак стрілки здаються найпростішим способом вирішення, тому метод може бути відновлений так:
this.pages.forEach(page => page.draw(this.settings));
Однак врахуйте, чи використовувався в коді бібліотеку типу jQuery, чиї методи thisспеціально пов'язуються . Тепер thisслід вирішити два значення:
Book.prototype.render = function () {
var book = this;
this.$pages.each(function (index) {
var $page = $(this);
book.draw(book.currentPage + index, $page);
});
};
Ми повинні використовувати functionдля того, eachщоб thisдинамічно зв’язувати . Тут ми не можемо використовувати функцію стрілки.
Зв'язок з декількома thisзначеннями також може бути заплутаним, оскільки важко знати, thisпро кого говорив автор:
function Reader() {
this.book.on('change', function () {
this.reformat();
});
}
Чи насправді автор мав намір подзвонити Book.prototype.reformat? Або він забув зв’язати this, і мав намір подзвонити Reader.prototype.reformat? Якщо ми змінимо обробник на функцію стрілки, ми будемо аналогічно задаватися питанням, чи бажав автор динаміки this, але вибрав стрілку, оскільки вона вміщується в одному рядку:
function Reader() {
this.book.on('change', () => this.reformat());
}
Можна поставити: "Чи винятковим є те, що стрілки іноді можуть бути неправильною функцією для використання? Можливо, якщо нам дуже рідко потрібні динамічні thisзначення, то все одно було б добре використовувати стрілки більшу частину часу".
Але запитайте себе так: "Чи варто" вартувати "налагодження коду та виявити, що результат помилки спричинив" кращий випадок? " 100% часу.
Є кращий спосіб: завжди використовувати function(тому thisзавжди можна динамічно зв'язати) та завжди посилатися thisчерез змінну. Змінні лексичні і припускають багато назв. Призначення thisзмінної дозволить зрозуміти ваші наміри:
function Reader() {
var reader = this;
reader.book.on('change', function () {
var book = this;
book.reformat();
reader.reformat();
});
}
Крім того, завжди присвоєння thisзмінної (навіть коли є одна thisчи відсутність інших функцій) забезпечує певні наміри залишатися зрозумілими навіть після зміни коду.
Також динаміка thisнавряд чи є винятковою. jQuery використовується на понад 50 мільйонах веб-сайтів (станом на цей текст у лютому 2016 року). Ось інші API, що thisдинамічно прив’язуються :
- Mocha (~ 120 к завантажень вчора) розкриває методи своїх тестів через
this.
- Grunt (~ 63 к завантаження вчора) розкриває методи складання завдань через
this.
- Хребет (~ 22 к. Завантажень вчора) визначає методи доступу
this.
- API - інтерфейси подій (як і DOM - х) відносяться до
EventTargetз this.
- Прототипні API, які виправлені або розширені, посилаються на екземпляри з
this.
(Статистика через http://trends.builtwith.com/javascript/jQuery та https://www.npmjs.com .)
Вам, швидше за все, потрібні динамічні thisприв’язки.
Лексика thisіноді очікується, але іноді ні; так само, як thisіноді очікується динаміка , але іноді ні. На щастя, є кращий спосіб, який завжди виробляє та повідомляє очікуване прив'язування.
Щодо короткого синтаксису
Функції стрілок досягли «коротшої синтаксичної форми» для функцій. Але чи зроблять вас ці коротші функції більш успішними?
Чи x => x * x"легше читати", ніж function (x) { return x * x; }? Можливо, це так, тому що швидше створити єдиний короткий рядок коду. Прихильність до Дайсона Вплив швидкості читання та довжини рядка на ефективність читання з екрана ,
Середня довжина рядка (55 символів на рядок), як видається, підтримує ефективне читання при нормальній та швидкій швидкості. Це призвело до найвищого рівня розуміння. . .
Аналогічні обґрунтування зроблені для умовного (потрійного) оператора та для однорядкових ifоператорів.
Однак ви справді пишете прості математичні функції, рекламовані у пропозиції ? Мої домени не є математичними, тому мої підпрограми рідко такі елегантні. Швидше за все, я бачу, як функції стрілок порушують ліміт стовпців і переходять на інший рядок завдяки редактору чи посібнику зі стилів, який зводить нанівець "читабельність" за визначенням Дайсона.
Можна поставити запитання: "Як щодо використання короткої версії для коротких функцій, коли це можливо?" Але тепер стилістичне правило суперечить мовному обмеженню: "Спробуйте використовувати найкоротші можливі позначення функцій, пам’ятаючи, що іноді лише найдовші позначення пов'язуватимуть, thisяк очікувалося". Така плутанина робить стрілки особливо схильними до неправильного використання.
Існує чимало проблем із синтаксисом функції стрілки:
const a = x =>
doSomething(x);
const b = x =>
doSomething(x);
doSomethingElse(x);
Обидві ці функції синтаксично дійсні. Але doSomethingElse(x);це не в тілі b, це лише погано відрезане твердження верхнього рівня.
Розширюючись до форми блоку, більше не існує неявного return, який можна було б забути відновити. Але вираз може тільки було призначене для отримання побічного ефекту, так що хто знає , якщо явно returnнеобхідно буде йти вперед?
const create = () => User.create();
const create = () => {
let user;
User.create().then(result => {
user = result;
return sendEmail();
}).then(() => user);
};
const create = () => {
let user;
return User.create().then(result => {
user = result;
return sendEmail();
}).then(() => user);
};
Те, що може бути призначено як параметр відпочинку, може бути проаналізовано як оператор розповсюдження:
processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest
Призначення можна переплутати з аргументами за замовчуванням:
const a = 1;
let x;
const b = x => {}; // No default
const b = x = a => {}; // "Adding a default" instead creates a double assignment
const b = (x = a) => {}; // Remember to add parens
Блоки мають вигляд об’єктів:
(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it's a labeled statement)
(id) => ({name: id}) // Returns an object
Що це означає?
() => {}
Чи мав намір автор створити неопераційний або функцію, яка повертає порожній об’єкт? (Маючи це на увазі, чи слід коли-небудь розміщувати {після цього =>? Чи слід обмежуватися лише синтаксисом виразів? Це ще більше зменшить частоту стрілок.)
=>виглядає <=і >=:
x => 1 ? 2 : 3
x <= 1 ? 2 : 3
if (x => 1) {}
if (x >= 1) {}
Щоб негайно викликати вираз функції стрілки, її потрібно розміщувати ()зовні, але розміщення ()на внутрішній стороні є дійсним і може бути навмисним.
(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function
Хоча, якщо писати (() => doSomething()());з наміром написати вираз функції, що негайно викликається, просто нічого не станеться.
Важко стверджувати, що функції стрілок «зрозуміліші», маючи на увазі всі вищезазначені випадки. Можна було б вивчити всі спеціальні правила, необхідні для використання цього синтаксису. Чи справді це варто?
Синтаксис functionвинятково узагальнений. Використовувати functionвиключно засоби, сама мова заважає писати заплутаний код. Писати процедури, які слід синтаксично розуміти у всіх випадках, я вибираю function.
Щодо настанови
Ви вимагаєте керівництва, яке повинно бути "чітким" та "послідовним". Використання функцій зі стрілками в кінцевому підсумку призведе до синтаксично допустимого, логічно недійсного коду, причому обидві форми функцій переплітаються, значущо та довільно. Тому я пропоную наступне:
Керівництво для функцій нотації в ES6:
- Завжди створюйте процедури за допомогою
function.
- Завжди призначати
thisзмінну. Не використовуйте () => {}.
Fixed this bound to scope at initialisationобмеженням?