Согласно предложению , стрелки направлены на «устранение и устранение нескольких общих болевых точек традиционных 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 (~ 120k загрузок вчера) предоставляет методы для своих тестов через
this.
- Grunt (~ 63k загрузок вчера) предоставляет методы для построения задач через
this.
- Backbone (~ 22 тыс. Загрузок вчера) определяет методы доступа
this.
- API событий (например, DOM) ссылаются на
EventTargetwith this.
- Прототипные API, которые исправлены или расширены, ссылаются на экземпляры с
this.
(Статистика через http://trends.builtwith.com/javascript/jQuery и https://www.npmjs.com .)
Вероятно, вам уже потребуются динамические thisпривязки.
thisИногда ожидается лексическое , а иногда нет; так же, как thisиногда ожидается динамика , но иногда нет. К счастью, есть лучший способ, который всегда производит и сообщает ожидаемую привязку.
Относительно краткого синтаксиса
Стрелкам функции удалось обеспечить «более короткую синтаксическую форму» для функций. Но сделают ли эти короткие функции вас более успешными?
Является ли x => x * x«легче читать» чем function (x) { return x * x; }? Может быть, потому что это более вероятно, чтобы создать одну короткую строку кода. В соответствии с Dyson's Влияние скорости чтения и длины строки на эффективность чтения с экрана ,
Средняя длина строки (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);
};
То, что может быть задано как параметр rest, может быть проанализировано как оператор распространения:
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это ограничением?