Согласно предложению , стрелки направлены на «устранение и устранение нескольких общих болевых точек традиционных 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) ссылаются на
EventTarget
with 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
это ограничением?