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

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

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

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

  • Не работает с new
  • Исправлено this, связанное с областью при инициализации

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

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

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

Ответы

Ответ 1

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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


Чтение функций стрелок

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

ECMAScript сильно изменился с тех пор, как ECMAScript 5.1 предоставил нам функциональные Array.forEach, Array.map и все эти функции функционального программирования, в которых мы используем функции, в которых раньше использовались for-loops. Асинхронный 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 для всего, что действительно должно быть методом класса или класса.


Примечания

  • Именованные функции стрелок отложены в спецификации ES6. Они могут быть добавлены в будущую версию.
  • В соответствии с проектом спецификации "объявления/выражения класса" создают конструкторскую функцию/пару прототипов точно так же, как и для объявлений функций ", если класс не использует ключевое слово extend. Небольшое различие заключается в том, что объявления классов являются константами, тогда как объявления функций не являются.
  • Примечание о блоках в одиночных операциях со стрелками: мне нравится использовать блок везде, где функция стрелки вызывается только для одного побочного эффекта (например, назначение). Таким образом, ясно, что возвращаемое значение может быть отброшено.

Ответ 2

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

Однако

  • Нельзя последовательно привязывать this лексически
  • Синтаксис функции Arrow нечеткий и неоднозначный

Следовательно, функции стрелок создают возможности для путаницы и ошибок и должны быть исключены из словаря программиста 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);
};

Функции Arrow намереваются исправить проблему, когда нам нужно получить доступ к свойству 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; }? Может быть, это так, потому что он, скорее всего, выпустит одну короткую строку кода. Accoring to Dyson Влияние скорости чтения и длины строки на эффективность чтения с экрана,

Средняя длина линии (55 символов в строке), по-видимому, поддерживает эффективное считывание с нормальной и быстрой скоростью. Это обеспечило высочайший уровень понимания.,.

Аналогичные обоснования выполняются для условного (тройного) оператора и для однострочных операторов if.

Однако вы действительно пишете простые математические функции рекламируемые в предложении? Мои домены не являются математическими, поэтому мои подпрограммы редко настолько элегантны. Скорее, я обычно вижу, как функции стрелок ломают предел столбца и переносят на другую строку из-за редактора или руководства по стилю, что сводит на нет "читаемость" по определению Dyson.

Можно было бы сказать: "Как насчет того, чтобы использовать краткую версию для коротких функций, когда это возможно?" Но теперь стилистическое правило противоречит языковым ограничениям: "Постарайтесь использовать кратчайшую нотацию функции, имея в виду, что иногда только самая длинная нотация будет связывать 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 a labeled statement)
(id) => ({name: id}) // Returns an object

Что это значит?

() => {}

Намерен ли автор создать no-op или функцию, которая возвращает пустой объект? (С учетом этого, должен ли мы когда-либо размещать { после =>? Должны ли мы ограничиться только синтаксисом выражения? Это еще больше уменьшит частоту стрелок.)

=> выглядит как <= и >=:

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 переменной. Не используйте () => {}.

Ответ 3

Функции со стрелками были созданы для упрощения 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 внутри methods function?

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

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

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

fn(); // [object Window]

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

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

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 которое ссылается на метод, к methods 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 функции стрелки ES6 мы можем решить проблему lexical scoping проще.

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

Actor.showMovies();

Arrow functions больше похожи на операторы функций, за исключением того, что они bind this с parent scope. Если arrow function is in top scope, this аргумент будет ссылаться на window/global scope, в то время как функция стрелки внутри обычной функции будет иметь этот аргумент такой же, как и ее внешняя функция.

С помощью функций arrow this связано с scope во время создания и не может быть изменено. Новый оператор 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 поскольку оно связано с lexical scoping ограничением.

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 определяется с помощью функции стрелки, но при вызове он оповещает о неопределенности, поскольку this.name не undefined как контекст остается в window.

Это происходит потому, что функция стрелки лексически связывает контекст с window object... т.е. с внешней областью. Выполнение 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 в вызове конструкции является вновь созданным объектом. При выполнении нового 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!');

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

Функция Arrow связывает 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.innerHTML = 'Clicked button' корректно изменяет текст кнопки, чтобы отразить состояние нажатия.

Ссылки: https://dmitripavlutin.com/when-not-to-use-arrow-functions-in-javascript/

Ответ 4

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

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

Функции стрелок НЕ должны использоваться:

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

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

Вариант 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, который является undefined в 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.

Ответ 5

В дополнение к большим ответам до сих пор я хотел бы представить совершенно другую причину, по которой функции стрелок в определенном смысле существенно лучше, чем обычные функции JavaScript. Для обсуждения предположим, что мы используем проверку типа типа TypeScript или Facebook "Поток". Рассмотрим следующий игрушечный модуль, который является действительным кодом ECMAScript 6 плюс плюс аннотации типа потока: (я буду включать нетипизированный код, который реалистично возникнет из 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;};
  }
}

Ответ 7

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

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

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

Мое убеждение заключается в следующем: мы не должны использовать this в первую очередь. Поэтому, если человек намеренно избегает использования this в своем коде, то свойство стрелок "лексическое this " мало что значит или не имеет значения. Кроме того, если исходить из того, что this плохо, обращение стрелок к this менее "хорошо"; вместо этого, это скорее форма контроля ущерба для другой черты плохого языка.

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

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

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

Относительно краткого синтаксиса

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

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

Относительно руководства

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

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

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

Ответ 8

Простым способом,

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();

Ans: Консоль будет печатать 20.

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

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

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

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

Ответ 9

Стрелка функции или Лямбда, были введено в 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 для вызова метода, но не objA когда определялся как функция arrow =>. Это происходит потому, что this в обычной функции, когда вызывается как метод на объекте (objA), является сам объект. Тем не менее, в случае функции со стрелкой, this становится лексически связанным с областью охвата this в которой она была определена (в нашем случае global/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() определен как функция, которая асинхронно вызывает console.log( [$ {this.id} → {this.name}] ) как обратный вызов в setTimeout, this Правильно разрешено в objB когда функция стрелки использовалась в качестве обратного вызова, но не objB когда обратный вызов определялся как обычная функция. Это связано с тем, что функция arrow => переданная в setTimeout(()=>..) закрыта над this лексически от своего родителя, т.е. вызов objB.print() который его определил. Другими словами, функция arrow => передана в setTimeout(()==>... привязана к objB как к this потому что при вызове objB.print() this был сам objB.

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

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

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

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

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

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

function print() { 
   console.log('[${this.id} -> {this.name}]');
}
const obj1 = {
 id: 10,
 name: "Simar",
 print // 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]

Ничто из вышеперечисленного не будет работать с функцией стрелки const print =() => { console.log( [$ {this.id} → {this.name}] );} как this может быть изменено и останется связанным с this объем, в котором он был определен (global/Window). Во всех этих примерах мы вызывали одну и ту же функцию с разными объектами (obj1 и obj2) один за другим, оба из которых были созданы после объявления функции print().

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

По этой причине функции constructor никогда не могут быть определены как функции стрелок, так как 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');
});

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

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


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

Ответ 10

Вскоре большинство людей согласятся:

  1. Используйте функцию в глобальной области видимости и для Object.prototype свойства.
  2. Используйте класс для конструкторов объектов.
  3. Используйте => везде иначе.

Как указано в https://www.sitepoint.com/es6-arrow-functions-new-fat-concise-syntax-javascript/