Когда мне следует использовать стрелочные функции в ECMAScript 6?

С () => {} и function () {} мы получаем два очень похожих способа написания функций в ES6. В других языках лямбда-функции часто отличаются тем, что они анонимны, но в ECMAScript любая функция может быть анонимной. Каждый из двух типов имеет уникальные области использования (а именно, когда this необходимо либо привязать явно, либо явно не связывать). Между этими доменами существует огромное количество случаев, когда подойдет любая нотация.

Стрелочные функции в ES6 имеют как минимум два ограничения:

  • Не работают с new и не могут использоваться при создании prototype
  • Исправлено this, привязанное к области видимости при инициализации

Помимо этих двух ограничений, стрелочные функции теоретически могут заменить обычные функции практически в любом месте. Как правильно использовать их на практике? Следует ли использовать стрелочные функции, например:

  • везде, где они работают, т.е. везде функция не обязана быть независимой от переменной this, и мы не создаем объект.
  • только везде, где они нужны, то есть прослушиватели событий, тайм-ауты, которые должны быть привязаны к определенной области
  • с "короткими" функциями, но не с "длинными" функциями
  • только с функциями, которые не содержат другой стрелочной функции

Я ищу руководство по выбору соответствующей нотации функции в будущей версии ECMAScript. Руководство должно быть четким, чтобы его можно было преподавать разработчикам в команде, и быть последовательным, чтобы не требовалось постоянного рефакторинга от одной нотации функции к другой.

Вопрос адресован людям, которые задумывались о стиле кода в контексте предстоящего ECMAScript 6 (Harmony) и уже работали с этим языком.


person lyschoening    schedule 08.04.2014    source источник
comment
Вы считаете Fixed this bound to scope at initialisation ограничением?   -  person thefourtheye    schedule 08.04.2014
comment
Это преимущество, но также может быть ограничением, если вы планируете повторно использовать функцию вне исходного контекста. Например, при динамическом добавлении функции в класс через Object.prototype. Под «ограничением» я подразумеваю то, что изменение значения this - это то, что вы можете делать с обычными функциями, но не со стрелочными функциями.   -  person lyschoening    schedule 08.04.2014
comment
Честно говоря, я считаю, что рекомендации по стилю кодирования довольно самоуверенны. Не поймите меня неправильно, я считаю, что они важны, но не существует единого правила, подходящего для всех.   -  person Felix Kling    schedule 13.04.2014
comment
Я не думаю, что Fixed this bound to scope at initialisation является ограничением. :) Взгляните на эту статью: explorejs.com/es6/ch_arrow-functions.html   -  person NgaNguyenDuy    schedule 05.02.2016
comment
Fixed this bound to scope at initialisation также известна как лексическая привязка   -  person Iven Marquardt    schedule 10.03.2016
comment
@thefourtheye, ограничение здесь означает ограничение, потому что тупой автоматический переводчик кода не может слепо заменить одно другим и предположить, что все будет работать, как ожидалось.   -  person Pacerier    schedule 20.04.2016


Ответы (9)


Некоторое время назад наша команда перенесла весь свой код (приложение AngularJS среднего размера) на JavaScript, скомпилированный с использованием Traceur Babel < / а>. Теперь я использую следующее эмпирическое правило для функций в ES6 и не только:

  • Используйте function в глобальной области и для свойств Object.prototype.
  • Используйте class для конструкторов объектов.
  • Используйте => везде.

Зачем использовать стрелочные функции почти везде?

  1. Безопасность области действия: при последовательном использовании стрелочных функций гарантируется, что все будет использовать тот же thisObject, что и корень. Если даже один стандартный обратный вызов функции смешан с кучей стрелочных функций, есть вероятность, что область видимости будет испорчена.
  2. Компактность: функции стрелок легче читать и писать. (Это может показаться самоуверенным, поэтому я приведу несколько примеров далее.)
  3. Ясность: когда почти все является стрелочной функцией, любой обычный function немедленно выделяется для определения области действия. Разработчик всегда может найти следующий по порядку function оператор, чтобы узнать, что такое thisObject.

Зачем всегда использовать обычные функции в глобальной области видимости или области видимости модуля?

  1. Чтобы указать функцию, которая не должна обращаться к thisObject.
  2. К объекту window (глобальная область видимости) лучше всего обращаться явно.
  3. Многие Object.prototype определения находятся в глобальной области видимости (например, String.prototype.truncate и т. Д.), И они в любом случае должны быть типа function. Последовательное использование function в глобальной области помогает избежать ошибок.
  4. Многие функции в глобальной области видимости являются конструкторами объектов для определений классов в старом стиле.
  5. Функции можно назвать 1. Это дает два преимущества: (1) писатьfunction foo(){} менее неудобно, чем const foo = () => {} - в частности, вне других вызовов функций. (2) Имя функции отображается в трассировке стека. Хотя было бы утомительно давать имя каждому внутреннему обратному вызову, наименование всех общедоступных функций, вероятно, является хорошей идеей.
  6. Объявления функций подняты (это означает, что к ним можно получить доступ до того, как они будут объявлены), что является полезным атрибутом в статической служебной функции.

Конструкторы объектов

Попытка создать экземпляр стрелочной функции вызывает исключение:

var x = () => {};
new x(); // TypeError: x is not a constructor

Таким образом, одно из ключевых преимуществ функций перед стрелочными функциями состоит в том, что функции действуют как конструкторы объектов:

function Person(name) {
    this.name = name;
}

Однако функционально идентичный 2 ECMAScript Harmony черновик определения класса почти такой же компактный:

class Person {
    constructor(name) {
        this.name = name;
    }
}

Я полагаю, что в конечном итоге использование прежних обозначений будет запрещено. Обозначение конструктора объекта может по-прежнему использоваться некоторыми для простых анонимных объектных фабрик, где объекты создаются программно, но не для чего-то еще.

Если требуется конструктор объекта, следует рассмотреть возможность преобразования функции в class, как показано выше. Синтаксис также работает с анонимными функциями / классами.

Читаемость стрелочных функций

Вероятно, лучшим аргументом в пользу использования обычных функций - будь к черту безопасность области видимости - было бы то, что стрелочные функции менее читабельны, чем обычные функции. Если ваш код изначально не работает, то стрелочные функции могут показаться ненужными, а когда стрелочные функции не используются последовательно, они выглядят некрасиво.

ECMAScript немного изменился с тех пор, как ECMAScript 5.1 дал нам функционал Array.forEach, Array.map и все эти функции функционального программирования, которые заставляют нас использовать функции, в которых раньше использовались циклы for. Асинхронный JavaScript стал немного популярнее. ES6 также будет поставлять объект Promise, который означает еще больше анонимных функций. Для функционального программирования пути назад нет. В функциональном JavaScript стрелочные функции предпочтительнее обычных.

Возьмем, к примеру, этот (особенно запутанный) фрагмент кода 3:

function CommentController(articles) {
    this.comments = [];

    articles.getList()
        .then(articles => Promise.all(articles.map(article => article.comments.getList())))
        .then(commentLists => commentLists.reduce((a, b) => a.concat(b)));
        .then(comments => {
            this.comments = comments;
        })
}

Тот же фрагмент кода с обычными функциями:

function CommentController(articles) {
    this.comments = [];

    articles.getList()
        .then(function (articles) {
            return Promise.all(articles.map(function (article) {
                return article.comments.getList();
            }));
        })
        .then(function (commentLists) {
            return commentLists.reduce(function (a, b) {
                return a.concat(b);
            });
        })
        .then(function (comments) {
            this.comments = comments;
        }.bind(this));
}

Хотя любую из стрелочных функций можно заменить стандартной функцией, от этого будет очень мало пользы. Какая версия читабельнее? Я бы сказал первый.

Я думаю, что вопрос, использовать ли стрелочные функции или обычные функции, со временем станет менее актуальным. Большинство функций либо станут методами класса, в которых отсутствует ключевое слово function, либо они станут классами. Функции останутся в использовании для исправления классов через Object.prototype. А пока я предлагаю зарезервировать ключевое слово function для всего, что действительно должно быть методом класса или классом.


Примечания

  1. Именованные стрелочные функции были в спецификации ES6. Они все еще могут быть добавлены в будущей версии.
  2. Согласно проекту спецификации, объявления / выражения класса создают пару функция-конструктор / прототип точно так же, как и объявления функций, если класс не использует ключевое слово extend. Незначительное отличие состоит в том, что объявления классов являются константами, а объявления функций - нет.
  3. Примечание о блоках в функциях стрелок с одним оператором: мне нравится использовать блок везде, где стрелочная функция вызывается только для побочного эффекта (например, присваивания). Таким образом становится ясно, что возвращаемое значение можно отбросить.
person lyschoening    schedule 13.04.2014
comment
Другой раз, когда вы захотите использовать function, это когда вы не хотите, чтобы this был связан, верно? Мой самый распространенный сценарий для этого - события, где вы можете захотеть, чтобы this ссылался на объект (обычно узел DOM), который вызвал событие. - person Brett; 31.07.2014
comment
@Brett: да, несвязанный объект this - это общий случай использования function. В событиях информация из this также будет в объекте события (event.currentTarget для событий DOM), но this может быть удобным средством доступа, если он вам не нужен для чего-то другого. - person lyschoening; 31.07.2014
comment
Поразмыслив над этим еще немного, я бы предпочел иметь привязку this для событий и нажимать эти несколько дополнительных нажатий клавиш для доступа к event.currentTarget, как вы предлагаете. Спасибо за ваш вопрос и ответ. - person Brett; 01.08.2014
comment
На самом деле я думаю, что в примере 3 обычные функции более читабельны. Даже непрограммисты могут предугадать, что происходит. Со стрелками вам нужно точно знать, как они работают, чтобы понять этот пример. Может быть, в примере со стрелкой поможет больше символов новой строки, но я не знаю. Всего мои 2 цента, но стрелки заставляют меня съеживаться (но я еще не использовал их, так что, возможно, скоро я обращусь). - person Spencer; 19.10.2015
comment
@Spencer, это справедливый аргумент. По моему собственному опыту, => со временем начинает выглядеть лучше. Я сомневаюсь, что непрограммисты будут по-разному относиться к этим двум примерам. Если вы пишете код ES2016, вы обычно не собираетесь использовать такое количество стрелочных функций. В этом примере, используя async / await и понимание массива, вы получите только одну стрелочную функцию в вызове reduce(). - person lyschoening; 20.10.2015
comment
@Spencer, все зависит от того, к какому привыкли. - person Pacerier; 20.04.2016
comment
Я полностью согласен со Спенсером в том, что обычные функции в этом примере гораздо более читабельны. - person jonschlinkert; 13.08.2016
comment
Я что-то упустил, или бит установки комментария не ведет себя по-разному в двух примерах (разное значение this)? - person Brian McCutchon; 24.08.2016
comment
@BrianMcCutchon, это зависит от реализации. Я добавлю .bind (), чтобы прояснить ситуацию. - person lyschoening; 24.08.2016
comment
Два приведенных выше комментария и редакция № 11 этого ответа демонстрируют, как неопределенность может возникнуть из-за прямой ссылки на this и использования стрелок. Если then связал this специально, код до этой версии (без bind) также мог бы быть правильным, и выбор между () => {} и function зависел бы не только от удобочитаемости. Это тот тип неопределенности, который я пытаюсь предотвратить с помощью рекомендаций в моем ответе. - person Jackson; 10.09.2016
comment
@Jackson Я думаю, вы неправильно поняли обсуждение. Была неопределенность с решением, в котором не использовались стрелки. - person lyschoening; 24.09.2016
comment
Неопределенность распространялась на оба решения; неясно, какое решение используется this правильно. Либо тот, что со стрелками, был правильным, и this должен был быть CommentsController, либо тот, у которого было function, был правильным, и this должен был быть тем, к чему then он привязан (может быть articles или что-то еще). - person Jackson; 24.09.2016
comment
Хороший ответ, спасибо! Лично я также максимально использую стрелки в глобальной области видимости. Это оставляет меня почти без «функции». Для меня «функция» в коде означает особый случай, который необходимо выделить и тщательно рассмотреть. - person kofifus; 29.08.2017
comment
@lyschoening this то же самое, что eventTarget при использовании привязки событий делегата? Я думал, что this будет в контексте элемента, к которому привязано событие, в то время как eventTarget находится в контексте элемента, вызвавшего событие. - person Nope; 15.06.2018
comment
Возможно, людям, которые думают, что обычные функции более читабельны, наверняка понравятся лишние пробелы. Возможно, пример => можно было бы сделать таким же отступом для эквивалентного сравнения? Хотя я все еще думаю, что => пример в его нынешнем виде вполне читабелен. - person Mateen Ulhaq; 16.05.2021

Согласно предложению, стрелки нацелены на устранение нескольких общих проблем традиционных функциональные выражения. Они намеревались улучшить положение за счет 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 (вчера ~ 22k загрузок) определяет методы доступа к 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 parentheses

Блоки выглядят как объекты:

(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 переменной. Не используйте () => {}.
person Jackson    schedule 25.01.2015
comment
Интересно написать о взгляде функционального программиста на JavaScript. Я не уверен, что согласен с аргументом о частных переменных. ИМО, мало кому они действительно нужны; тем, кто это делает, вероятно, также потребуются другие функции контракта, и они все равно выберут расширение языка, такое как TypeScript. Я определенно вижу привлекательность self вместо this. Указанные вами подводные камни стрелочной функции также допустимы, и здесь также определенно применимы те же стандарты, что и для других операторов, которые могут обходиться без фигурных скобок; в противном случае, я думаю, с вашим аргументом можно было бы с таким же успехом отстаивать стрелочные функции повсюду. - person lyschoening; 26.01.2015
comment
Я пересмотрел свой тезис, чтобы прояснить, что я выступаю против использования стрелочных функций где угодно: с this вне картинки, стрелочные функции и function имеют почти равную ценность; за исключением того, что из-за более строгого синтаксиса function немного лучше. Так что всегда используйте это. - person Jackson; 03.02.2015
comment
Наличие нескольких способов ведения дел создает ненужные векторы для споров и разногласий на рабочем месте и в языковом сообществе. Было бы лучше, если бы грамматика языка не позволяла нам делать неправильный выбор. Согласен так много. Хорошая запись! Я думаю, что стрелочные функции - это на самом деле шаг назад. По другой теме, я бы хотел, чтобы мои коллеги перестали пытаться превратить JavaScript в C # с помощью ряда определений .prototype. Это отвратительно. Я должен анонимно связать ваш пост :) - person Spencer; 19.10.2015
comment
@lyschoening, я добавил дополнительные аргументы в надежде убедить вас в важности конфиденциальности и неизменности по умолчанию, которые помимо важности также продвигают идею о том, что функции, возвращающие объекты, лучше, чем прототипы, что дополнительно устраняет необходимость в this и, таким образом, разрушает одну основу для добавления стрелочных функций к языку. - person Jackson; 12.01.2016
comment
@Jackson Людям нужно время, чтобы понять, что они должны сделать () => ({id: 123}) в последнем добавленном вами пункте. Мой опыт перевода нескольких разработчиков на ES6 показывает, что они сначала пишут () => { return {id: 123}}. Синтаксису можно научить. Как человек Python, мне также не нравится выражение new, но, увы, JavaScript - это не Python. На мой взгляд, чтобы быть продуктивным, нужно работать с тем, что предоставляет язык. Разработчик JS, заботящийся о неизменности и желающий в значительной степени избегать this, может использовать, например, Immutable.js и по-прежнему наслаждайтесь компактностью синтаксиса стрелок. - person lyschoening; 12.01.2016
comment
Очень хорошо написано! Хотя я не согласен с большинством ваших пунктов, важно учитывать противоположную точку зрения. - person minexew; 08.03.2016
comment
Не стрелочные функции, а странное поведение this - это проблема Javascript. Вместо неявной привязки this следует передавать как явный аргумент. - person Iven Marquardt; 10.03.2016
comment
@rand Точно мои мысли, хотя я бы обвинял jQuery и другие библиотеки в злоупотреблении this, а не в использовании JavaScript this. Похоже, они просто навязывают вам параметр, который вы не можете назвать. - person Brian McCutchon; 24.08.2016
comment
Всегда используйте функцию (чтобы ее всегда можно было привязать динамически) и всегда ссылайтесь на нее через переменную.. Я не могу больше не согласиться! - person ; 08.09.2016
comment
В некоторых других языках с короткими функциями перевод строки часто ставится после знака равенства: const a = *newline* x => doSomething(x). Не очень хорошо работает в комментариях; просто вставьте его куда-нибудь, чтобы узнать, что вы думаете. - person Levi Morrison; 27.09.2016
comment
Хорошо написано, мне понравилось читать. Однако я не могу не чувствовать, что аргументы против сводятся к предпочтению исторического поведения без введения нового поведения и простому незнанию нового. Это исходило от кого-то, кто сначала ненавидел синтаксис толстой стрелки. Но нельзя допускать развития языков, а стрелочные функции решают проблемы. Вы не окажете себе одолжение (здесь, в 2018 году), если будете следовать этому руководству и никогда не использовать стрелочные функции. - person Aaron Beall; 25.04.2018
comment
@Aaron New не обязательно лучше. Я взвесил преимущества нового пути по сравнению со старым. Я обнаружил, что старый способ должен давать правильные результаты чаще, чем новый. Я думаю, что ценой того, что вы будете модными, вы все равно окажете себе услугу, отказавшись от этой функции. - person Jackson; 25.04.2018

Стрелочные функции были созданы для упрощения функций scope и решение ключевого слова this, сделав его проще. Они используют синтаксис =>, который выглядит как стрелка.

Примечание. Он не заменяет существующие функции. Если вы замените синтаксис каждой функции стрелочными функциями, он будет работать не во всех случаях.

Давайте посмотрим на существующий синтаксис ES5. Если бы ключевое слово this находилось внутри метода объекта (функции, принадлежащей объекту), на что оно могло бы ссылаться?

var Actor = {
  name: 'RajiniKanth',
  getName: function() {
     console.log(this.name);
  }
};
Actor.getName();

Приведенный выше фрагмент будет относиться к object и распечатывать имя "RajiniKanth". Давайте рассмотрим приведенный ниже фрагмент и посмотрим, на что он здесь указывает.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach(function(movie) {
     alert(this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

А как насчет того, чтобы ключевое слово this находилось внутри method’s function?

Здесь это будет относиться к window object, чем к inner function, поскольку оно выпало из scope. Поскольку this всегда ссылается на владельца функции, в которой он находится, в этом случае - поскольку он теперь вне области видимости - объект window / global.

Когда он находится внутри метода object, владельцем function является объект. Таким образом, ключевое слово this привязано к объекту. Тем не менее, когда он находится внутри функции, автономно или внутри другого метода, он всегда будет ссылаться на объект window/global.

var fn = function(){
  alert(this);
}

fn(); // [object Window]

Есть способы решить эту проблему в самом нашем ES5. Давайте посмотрим на это, прежде чем погрузиться в стрелочные функции ES6, чтобы решить, как это решить.

Обычно вы создаете переменную вне внутренней функции метода. Теперь метод ‘forEach’ получает доступ к this и, следовательно, к свойствам object’s и их значениям.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   var _this = this;
   this.movies.forEach(function(movie) {
     alert(_this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

Использование bind для присоединения ключевого слова this, которое относится к методу, к method’s inner function.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach(function(movie) {
     alert(this.name + " has acted in " + movie);
   }.bind(this));
  }
};

Actor.showMovies();

Теперь с помощью стрелочной функции ES6 мы можем решить проблему лексической области видимости более простым способом.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach((movie) => {
     alert(this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

Стрелочные функции больше похожи на операторы функций, за исключением того, что они привязывают this к родительской области. Если стрелочная функция находится в верхней области, аргумент this будет ссылаться на окно / глобальную область, а стрелочная функция внутри обычной функции будет иметь аргумент this как так же, как и его внешняя функция.

С помощью стрелки функции this привязаны к охватывающей области во время создания и не могут быть изменены. Операторы new, bind, call и apply на это не влияют.

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
  callback(param);
  }, 1);
};

// With a traditional function if we don't control
// the context then can we lose control of `this`.
var o = {
  doSomething: function () {
  // Here we pass `o` into the async function,
  // expecting it back as `param`
  asyncFunction(o, function (param) {
  // We made a mistake of thinking `this` is
  // the instance of `o`.
  console.log('param === this?', param === this);
  });
  }
};

o.doSomething(); // param === this? false

В приведенном выше примере мы потеряли контроль над этим. Мы можем решить приведенный выше пример, используя ссылку на переменную this или используя bind. С ES6 становится проще управлять this, привязанным к лексической области видимости.

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
  callback(param);
  }, 1);
};

var o = {
  doSomething: function () {
  // Here we pass `o` into the async function,
  // expecting it back as `param`.
  //
  // Because this arrow function is created within
  // the scope of `doSomething` it is bound to this
  // lexical scope.
  asyncFunction(o, (param) => {
  console.log('param === this?', param === this);
  });
  }
};

o.doSomething(); // param === this? true

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

Внутри литерала объекта.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  getName: () => {
     alert(this.name);
  }
};

Actor.getName();

Actor.getName определяется с помощью функции стрелки, но при вызове она предупреждает undefined, потому что this.name равно undefined, поскольку контекст остается для window.

Это происходит потому, что стрелочная функция лексически связывает контекст с _40 _..., то есть с внешней областью видимости. Выполнение this.name эквивалентно window.name, которое не определено.

Прототип объекта

То же правило применяется при определении методов для prototype object. Вместо использования стрелочной функции для определения метода sayCatName, который возвращает неверный context window:

function Actor(name) {
  this.name = name;
}
Actor.prototype.getName = () => {
  console.log(this === window); // => true
  return this.name;
};
var act = new Actor('RajiniKanth');
act.getName(); // => undefined

Вызов конструкторов

this в вызове конструкции - это вновь созданный объект. При выполнении new Fn () контекст constructor Fn - это новый объект: this instanceof Fn === true.

this настраивается из включающего контекста, то есть из внешней области видимости, которая не позволяет назначать его вновь созданному объекту.

var Message = (text) => {
  this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message('Hello World!');

Обратный вызов с динамическим контекстом

Стрелочная функция связывает context статически при объявлении и не может сделать его динамическим. Присоединение слушателей событий к элементам DOM - обычная задача в программировании на стороне клиента. Событие запускает функцию обработчика с этим в качестве целевого элемента.

var button = document.getElementById('myButton');
button.addEventListener('click', () => {
  console.log(this === window); // => true
  this.innerHTML = 'Clicked button';
});

this - это окно в стрелочной функции, которая определена в глобальном контексте. Когда происходит событие щелчка, браузер пытается вызвать функцию-обработчик с контекстом кнопки, но функция стрелки не изменяет свой предопределенный контекст. this.innerHTML эквивалентно window.innerHTML и не имеет смысла.

Вы должны применить выражение функции, которое позволяет изменять это в зависимости от целевого элемента:

var button = document.getElementById('myButton');
button.addEventListener('click', function() {
  console.log(this === button); // => true
  this.innerHTML = 'Clicked button';
});

Когда пользователь нажимает кнопку, this в функции обработчика является кнопкой. Таким образом, this.innerHTML = 'Clicked button' правильно изменяет текст кнопки, чтобы отразить состояние нажатия.

использованная литература

person Thalaivar    schedule 15.10.2016
comment
Что ж, я должен признать, что лучшее находится посередине. Проголосовано за утверждение, что стрелочные функции не будут охватывать любые возможные варианты использования функций. Они действительно предназначены для решения лишь части общих проблем. Просто переключиться на них полностью будет излишним. - person BlitZ; 16.10.2016
comment
@DmitriPavlutin: Проверьте мой обновленный пост, это собрание множества вещей ... может быть, мне стоит опубликовать ссылку. - person Thalaivar; 16.03.2017
comment
Ваш код после строки «использование привязки для присоединения ключевого слова this, которое относится к методу внутренней функции метода». в нем есть ошибки. Вы проверили остальные свои примеры? - person Isaac Pak; 15.05.2017
comment
Один using bind to attach the this keyword that refers to the method to the method’s inner function. имеет синтаксические ошибки. - person Coda Chang; 08.02.2020
comment
Должно быть var Actor = { name: 'RajiniKanth', movies: ['Kabali', 'Sivaji', 'Baba'], showMovies: function() { this.movies.forEach(function(movie){ alert(this.name + ' has acted in ' + movie); }.bind(this)) } }; Actor.showMovies(); - person Coda Chang; 08.02.2020
comment
Отличный ответ! Есть ли причина, по которой вы сделали asyncFunction () стрелочной функцией? Или это единственная причина, потому что он для вас чище / удобнее для чтения? - person Empi; 14.05.2020
comment
В вашем последнем случае с addEventListener вы все равно можете получить объем щелчка с помощью event.target даже при использовании стрелочных функций. Мысли? - person philosopher; 02.11.2020

Стрелочные функции - до сих пор наиболее широко используемая функция ES6 ...

Использование: все функции ES5 следует заменить стрелочными функциями ES6, за исключением следующих сценариев:

Стрелочные функции не использовать:

  1. When we want function hoisting
    • as arrow functions are anonymous.
  2. When we want to use this/arguments in a function
    • as arrow functions do not have this/arguments of their own, they depend upon their outer context.
  3. When we want to use named function
    • as arrow functions are anonymous.
  4. When we want to use function as a constructor
    • as arrow functions do not have their own this.
  5. When we want to add function as a property in object literal and use object in it
    • as we can not access this (which should be object itself).

Давайте разберемся с некоторыми вариантами стрелочных функций, чтобы лучше понять:

Вариант 1: когда мы хотим передать функции более одного аргумента и вернуть из нее какое-то значение.

Версия ES5:

var multiply = function (a, b) {
    return a*b;
};
console.log(multiply(5, 6)); // 30

Версия ES6:

var multiplyArrow = (a, b) => a*b;
console.log(multiplyArrow(5, 6)); // 30

Примечание:

Ключевое слово function не требуется. => является обязательным. {} являются необязательными, когда мы не предоставляем {} return неявно добавляется JavaScript, а когда мы предоставляем {}, нам нужно добавить return, если он нам нужен.

Вариант 2: когда мы хотим передать только один аргумент функции и вернуть из него какое-то значение.

Версия ES5:

var double = function(a) {
    return a*2;
};
console.log(double(2)); // 4

Версия ES6:

var doubleArrow  = a => a*2;
console.log(doubleArrow(2)); // 4

Примечание:

При передаче только одного аргумента мы можем опустить круглые скобки, ().

Вариант 3: когда мы не хотим передавать какой-либо аргумент функции и не хотим возвращать какое-либо значение.

Версия ES5:

var sayHello = function() {
    console.log("Hello");
};
sayHello(); // Hello

Версия ES6:

var sayHelloArrow = () => {console.log("sayHelloArrow");}
sayHelloArrow(); // sayHelloArrow

Вариант 4: когда мы хотим явно вернуться из стрелочных функций.

Версия ES6:

var increment = x => {
  return x + 1;
};
console.log(increment(1)); // 2

Вариант 5: когда мы хотим вернуть объект из стрелочных функций.

Версия ES6:

var returnObject = () => ({a:5});
console.log(returnObject());

Примечание:

Нам нужно заключить объект в круглые скобки, (). В противном случае JavaScript не сможет отличить блок от объекта.

Вариант 6. Стрелочные функции не имеют собственного arguments (массив, подобный объекту). Они зависят от внешнего контекста для arguments.

Версия ES6:

function foo() {
  var abc = i => arguments[0];
  console.log(abc(1));
};
foo(2); // 2

Примечание:

foo - это функция ES5, с массивом arguments, подобным объекту, и аргументом, переданным ей, является 2, поэтому arguments[0] для foo равно 2.

abc - это стрелочная функция ES6, поскольку у нее нет собственного arguments. Следовательно, вместо этого он печатает arguments[0] из foo своего внешнего контекста.

Вариант 7: стрелочные функции не имеют this самих себя, они зависят от внешнего контекста для this

Версия ES5:

var obj5 = {
  greet: "Hi, Welcome ",
  greetUser : function(user) {
        setTimeout(function(){
        console.log(this.greet + ": " +  user); // "this" here is undefined.
        });
     }
};

obj5.greetUser("Katty"); //undefined: Katty

Примечание:

Обратный вызов, переданный в setTimeout, является функцией ES5 и имеет свой собственный this, который не определен в среде use-strict. Отсюда получаем вывод:

undefined: Katty

Версия ES6:

var obj6 = {
  greet: "Hi, Welcome ",
  greetUser : function(user) {
    setTimeout(() => console.log(this.greet + ": " +  user));
      // This here refers to outer context
   }
};

obj6.greetUser("Katty"); // Hi, Welcome: Katty

Примечание:

Обратный вызов, переданный в setTimeout, является стрелочной функцией ES6, и у нее нет своего собственного this, поэтому он берет ее из своего внешнего контекста, то есть greetUser, который имеет this. Это obj6, и поэтому мы получаем результат:

Hi, Welcome: Katty

Разное:

  • Мы не можем использовать new со стрелочными функциями.
  • Стрелочные функции не имеют свойство prototype.
  • У нас нет привязки this, когда стрелочная функция вызывается через apply или call.
person Manishz90    schedule 24.09.2017

Я по-прежнему поддерживаю все, что написал в моем первом ответе в этой теме. Однако с тех пор мое мнение о стиле кода изменилось, поэтому у меня есть новый ответ на этот вопрос, основанный на моем последнем.

Что касается лексического this

В моем последнем ответе я сознательно отказался от основного убеждения, которое я придерживаюсь в отношении этого языка, поскольку оно не имело прямого отношения к аргументу, который я приводил. Тем не менее, без явного указания этого, я могу понять, почему многие люди просто не согласны с моей рекомендацией не использовать стрелки, когда они находят стрелки настолько полезными.

Я считаю, что мы вообще не должны использовать this. Следовательно, если человек сознательно избегает использования this в своем коде, то «лексическая this» особенность стрелок не имеет большого значения или вообще не имеет значения. Кроме того, исходя из того, что this - это плохо, то, как стрелка обращается с this, менее "хорошо"; вместо этого, это скорее средство защиты от другой ненормативной лексики.

Я полагаю, что это либо не приходит в голову некоторым людям, но даже тем, с кем это происходит, они должны неизменно обнаруживать себя работающими в кодовых базах, где this появляется сто раз для каждого файла, и небольшой (или большой) контроль повреждений является все, на что мог надеяться разумный человек. Таким образом, стрелы могут быть хорошими в некотором смысле, когда они улучшают плохую ситуацию.

Даже если легче написать код с this со стрелками, чем без них, правила использования стрелок остаются очень сложными (см. Текущий поток). Таким образом, рекомендации не являются ни «четкими», ни «последовательными», как вы просили. Даже если программисты знают о двусмысленности стрелок, я думаю, они пожимают плечами и все равно принимают их, потому что значение лексического this затмевает их.

Все это является предисловием к следующему осознанию: если не использовать this, то двусмысленность в отношении this, которую обычно вызывают стрелки, становится неактуальной. Стрелки в этом контексте становятся более нейтральными.

Что касается краткого синтаксиса

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

Другими словами: черт возьми, мне тоже нужны однострочные функции!

Что касается руководства

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

Рекомендации по обозначению функций в ES6:

  • Не используйте this.
  • Используйте объявления функций для функций, которые вы вызываете по имени (потому что они поднимаются).
  • Используйте стрелочные функции для обратных вызовов (потому что они, как правило, короче).
person Jackson    schedule 06.06.2019
comment
Согласитесь на 100% с вашим Руководством по нотации функций в разделе ES6 внизу - особенно с подъемными и встроенными функциями обратного вызова. хороший ответ! - person Jeff McCloud; 29.06.2019

В дополнение к отличным ответам я хотел бы представить совсем другую причину, по которой стрелочные функции в определенном смысле фундаментально лучше, чем обычные функции JavaScript.

Для обсуждения давайте временно предположим, что мы используем средство проверки типов, например TypeScript или Facebook Flow. Рассмотрим следующий игрушечный модуль, который представляет собой действительный код ECMAScript 6 плюс аннотации типа Flow (я включу нетипизированный код, который реально будет получен из Babel, в конце этого ответа, чтобы его действительно можно было запустить):

export class C {
  n : number;
  f1: number => number;
  f2: number => number;

  constructor(){
    this.n = 42;
    this.f1 = (x:number) => x + this.n;
    this.f2 = function (x:number) { return  x + this.n;};
  }
}

Теперь посмотрим, что происходит, когда мы используем класс C из другого модуля, например:

let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1: number = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2: number = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!

Как видите, здесь проверка типов завершилась неудачно: f2 должен был вернуть число, но вернул строку!

Хуже того, кажется, что никакое средство проверки мыслимого типа не может обрабатывать обычные (не стрелочные) функции JavaScript, потому что this из f2 не встречается в списке аргументов f2, поэтому требуемый тип для этого не может возможно, будет добавлен как аннотация к f2.

Влияет ли эта проблема на людей, которые не используют средства проверки типов? Я так думаю, потому что даже когда у нас нет статических типов, мы думаем, как будто они есть. (Первый параметр должен быть числом, второй - строкой и т. Д.) Скрытый аргумент this, который может или не может использоваться в теле функции, усложняет наш мысленный учет.

Вот работающая нетипизированная версия, которая будет произведена Babel:

class C {
    constructor() {
        this.n = 42;
        this.f1 = x => x + this.n;
        this.f2 = function (x) { return x + this.n; };
    }
}

let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1 = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2 = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!

person Carsten Führmann    schedule 12.03.2017

Я предпочитаю всегда использовать стрелочные функции, когда доступ к локальному this не требуется, потому что стрелочные функции не связывают свои собственные this, arguments, super или new.target.

person zowers    schedule 02.05.2017
comment
Супер буквально? - person Peter Mortensen; 14.12.2020

Стрелочные функции или лямбды были введены в ES 6. Помимо элегантности и минимального синтаксиса, наиболее заметным функциональным отличием является определение this функции стрелки

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

В стрелочных функциях this лексически связан, что означает, что он закрывается более чем на this из области, в которой была определена стрелочная функция (родительская область), и не изменяется. независимо от того, где и как он вызывается / вызывается.

Ограничения стрелочных функций как методов объекта

// this = global Window
let objA = {
  id: 10,
  name: "Simar",
  print () { // same as print: function()
    console.log(`[${this.id} -> ${this.name}]`);
  }
}

objA.print(); // logs: [10 -> Simar]

objA = {
  id: 10,
  name: "Simar",
  print: () => {
    // Closes over this lexically (global Window)
    console.log(`[${this.id} -> ${this.name}]`);
  }
};

objA.print(); // logs: [undefined -> undefined]

В случае objA.print(), когда print() метод был определен с использованием обычного function, он работал, разрешая this правильно в objA для вызова метода, но не работал, когда определялся как функция arrow=>. Это потому, что this в обычной функции, когда она вызывается как метод объекта (objA), является самим объектом.

Однако в случае стрелочной функции this лексически привязывается к this охватывающей области, в которой она была определена (глобальная / Window в нашем случае), и остается неизменной во время своего вызова в качестве метода на objA.

Есть преимущества стрелочных функций по сравнению с обычными функциями в методе (ах) объекта, но только тогда, когда ожидается, что this будет зафиксирован и привязан во время определения.

/* this = global | Window (enclosing scope) */

let objB = {
  id: 20,
  name: "Paul",
  print () { // Same as print: function()
    setTimeout( function() {
      // Invoked async, not bound to objB
      console.log(`[${this.id} -> ${this.name}]`);
    }, 1)
  }
};

objB.print(); // Logs: [undefined -> undefined]'

objB = {
  id: 20,
  name: "Paul",
  print () { // Same as print: function()
    setTimeout( () => {
      // Closes over bind to this from objB.print()
      console.log(`[${this.id} -> ${this.name}]`);
    }, 1)
  }
};

objB.print(); // Logs: [20 -> Paul]

В случае objB.print(), где метод print() определен как функция, которая вызывает _21 _ [$ {this.id} - ›{this.name}] _ 22_ асинхронно в качестве обратного вызова на setTimeout, this правильно разрешается в objB, когда функция стрелки использовался как обратный вызов, но потерпел неудачу, когда обратный вызов был определен как обычная функция.

Это потому, что функция стрелки =>, переданная в setTimeout(()=>..), закрыла this лексически от своего родителя, то есть вызов objB.print(), который ее определил. Другими словами, функция стрелки => передана в setTimeout(()==>..., привязана к objB как ее this, потому что вызов objB.print() this был сам objB.

Мы могли бы легко использовать Function.prototype.bind(), чтобы заставить обратный вызов, определенный как обычная функция, работать, привязав его к правильному this.

const objB = {
  id: 20,
  name: "Singh",
  print () { // The same as print: function()
    setTimeout( (function() {
      console.log(`[${this.id} -> ${this.name}]`);
    }).bind(this), 1)
  }
}

objB.print() // logs: [20 -> Singh]

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

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

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

/* this = global | Window (enclosing scope) */

function print() {
  console.log(`[${this.id} -> {this.name}]`);
}

const obj1 = {
  id: 10,
  name: "Simar",
  print // The same as print: print
};

obj.print(); // Logs: [10 -> Simar]

const obj2 = {
  id: 20,
  name: "Paul",
};

printObj2 = obj2.bind(obj2);
printObj2(); // Logs: [20 -> Paul]
print.call(obj2); // logs: [20 -> Paul]

Ничего из вышеперечисленного не будет работать со стрелочной функцией _44 _ [$ {this.id} - ›{this.name}] _ 45_, поскольку this не может быть изменено и останется привязанным к this охватывающей области, в которой она была определена (глобальная / Окно).

Во всех этих примерах мы вызывали одну и ту же функцию с разными объектами (obj1 и obj2) один за другим, оба из которых были созданы после объявления функции print().

Это были надуманные примеры, но давайте подумаем еще о примерах из реальной жизни. Если бы нам пришлось написать наш reduce() метод, аналогичный тому, который работает с arrays, мы снова не сможем определить его как лямбда, потому что он должен вывести this из контекста вызова, то есть массива, для которого он был вызван.

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

Кроме того, когда фреймворки или системы принимают функцию (-ы) обратного вызова для последующего вызова с динамическим контекстом this, мы не можем использовать стрелочные функции, поскольку снова this может потребоваться изменение при каждом вызове. Эта ситуация обычно возникает с обработчиками событий DOM.

'use strict'
var button = document.getElementById('button');

button.addEventListener('click', function {
  // web-api invokes with this bound to current-target in DOM
  this.classList.toggle('on');
});

var button = document.getElementById('button');

button.addEventListener('click', () => {
  // TypeError; 'use strict' -> no global this
  this.classList.toggle('on');
});

Это также причина того, почему в таких фреймворках, как Angular 2+ и Vue.js, ожидается, что методы привязки компонентов шаблона будут обычными функциями / методами, поскольку this их вызов управляется. каркасами для связывающих функций. (Angular использует Zone.js для управления асинхронным контекстом для вызовов функций привязки представления и шаблона.)

С другой стороны, в React, когда мы хотим передать метод компонента в качестве обработчика события, например <input onChange={this.handleOnchange} />, мы должны определить handleOnchanage = (event)=> {this.props.onInputChange(event.target.value);} как стрелочную функцию для каждого вызова. Мы хотим, чтобы это был тот же экземпляр компонента, который создал JSX для визуализированного элемента DOM.


Эта статья также доступна на моя публикация в Medium. Если вам понравилась статья или у вас есть комментарии и предложения, хлопайте или оставьте комментарии на Medium.

person Simar Singh    schedule 24.03.2019

Проще говоря,

var a = 20; function a() {this.a = 10; console.log(a);}
//20, since the context here is window.

Другой пример:

var a = 20;
function ex(){
    this.a = 10;
    function inner(){
        console.log(this.a); // Can you guess the output of this line?
    }
    inner();
}
var test = new ex();

Ответ: консоль напечатает 20.

Причина в том, что всякий раз, когда функция выполняется, создается ее собственный стек, в этом примере функция ex выполняется с оператором new, поэтому будет создан контекст, а при выполнении inner JavaScript создаст новый стек и выполнит функцию inner. в global context, хотя есть локальный контекст.

Итак, если мы хотим, чтобы у функции inner был локальный контекст, которым является ex, тогда нам нужно привязать контекст к внутренней функции.

Стрелки решают эту проблему. Вместо того, чтобы брать Global context, они берут local context, если таковые существуют. В * данном примере new ex() будет равно this.

Таким образом, во всех случаях, когда привязка является явной, стрелки по умолчанию решают проблему.

person Rajendra kumar Vankadari    schedule 21.07.2017