Php стрелочные функции. ES6. Стрелочные функции в javascript. Ловушка: как не забыть про new

  • Tutorial

Одной из самых интересных частей нового стандарта ECMAScript 6 являются стрелочные функции. Стрелочные функции, как и понятно из названия определяются новым синтаксисом, который использует стрелку => . Однако, помимо отличного синтаксиса, стрелочные функции отличаются от традиционных функций и в других моментах:

  • Лексическое связывание. Значения специальных переменных this , super и arguments определяются не тем, как стрелочные функции были вызваны, а тем, как они были созданы.
  • Неизменяемые this , super и arguments . Значения этих переменных внутри стрелочных функций остаются неизменными на протяжении всего жизненного цикла функции.
  • Стрелочные функции не могут быть использованы как конструктор и кидают ошибку при использовании с оператором new .
  • Недоступность «собственного» значения переменной arguments .
Было несколько причин для введения этих отличий. Первоочередная - это то, что связывание (binding) используется довольно часто в JavaScript. Очень легко потерять нужное значение this при использовании традиционных функций, что может привести к непредсказуемым последствиям. Другая причина, это то, что JS-движки смогут легко оптимизировать выполнение стрелочных функций за счет этих ограничений (в противоположность традиционным функциям, которые могут быть использованы в качестве конструктора и которые свободны для модификации специальных переменных ).


Примечание: Данная статья - это компиляция из вольного перевода статьи Understanding ECMAScript 6 arrow functions и чтения последнего черновика спецификации (January 20, 2014 Draft Rev 22).

Синтаксис В общем случае, синтаксис стрелочных функций выглядит так:

Var fun = (x) => x;
Он очень похож на аналогичный синтаксис в таких языках как Scala, CoffeeScript и на синтаксис lambda-выражений из C#.

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

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

Var reflect = value => value; // эквивалент var reflect = function(value) { return value; }
Когда у стрелочной функции только один аргумент, то он может быть объявлен без скобок. Следующее после стрелки тело функции также может быть без фигурных скобок и может не содержать ключевого слова return .

Несколько параметров Но если вы хотите объявить более одного параметра, то должны обрамить список параметров в круглые скобки:

Var sum = (num1, num2) => num1 + num2; // эквивалент var sum = function(num1, num2) { return num1 + num2; };
Функция sum просто суммирует два аргумента. Единственное отличие от предыдущего примера в наличии круглых скобок и запятой (прямо как в традиционных функциях).

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

Var sum = () => 1 + 2; // эквивалент var sum = function() { return 1 + 2; };

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

Var sum = (num1, num2) => { return num1 + num2; } // эквивалент var sum = function(num1, num2) { return num1 + num2; };
Тело функции будет обработано точно так же, как и в случае классических функций, за исключением того, что значения специальных переменных this , super и arguments будут вычисляться по-другому.

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

Var getTempItem = id => ({ id: id, name: "Temp" }); // эквивалент var getTempItem = function(id) { return { id: id, name: "Temp" } };
Помещение литерала объекта в круглые скобки указывает парсеру, что фигурные скобки это не начало традиционного синтаксиса для тела функции, а начало литерала.

Переменное число параметров Так как «собственный» объект arguments не доступен внутри стрелочной функции (значение arguments лексически связано со значением arguments традиционной функции, внутри которой стрелочная функция была объявлена), то для стрелочных функций с переменным числом параметров нужно использовать rest -паттерн из шаблонов деструктуризации . Пример:

Var getTempItems = (...rest) => rest; // эквивалент var getTempItems = function() { return .slice.apply(arguments) };

Шаблон деструктуризации в качестве параметраВ рамках данной статьи мы не рассматриваем шаблоны деструктуризации - вы можете почитать про них в статье Обзор ECMAScript 6, следующей версии JavaScript , хотя эта информация частично устарела.

Как видно из предыдущего примера, несмотря на то, что у стрелочной функции всего один аргумент, всё равно необходимо применять круглые скобки при использовании шаблонов деструктуризации как единственного параметра функции. Примеры с другими шаблонами :

Var a = ({a}) => a; var b = ([b]) => b;

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

Var pageHandler = { id: "123456" , init: function() { document.addEventListener("click", function(event) { this.doSomething(event.type); // ошибка }); } , doSomething: function(type) { console.log("Handling " + type + " for " + this.id) } };
В приведённом коде объект pageHandler должен обрабатывать клики на странице. Метод init() навешивает обработчик на нужное событие, который внутри себя вызывает this.doSomething() . Однако код отработает неправильно. Ссылка на this.doSomething() не валидна, поскольку this указывает на объект document внутри обработчика события вместо планируемого pageHandler . При попытке выполнить этот код, вы получите ошибку, поскольку объект document не имеет метода doSomething .

Вы можете завязать значение this на объекте pageHandler используя handleEvent или вызвав у функции стандартный метод bind() :

Var pageHandler = { id: "123456" , init: function() { document.addEventListener("click", (function(event) { this.doSomething(event.type); // error }).bind(this)); } , doSomething: function(type) { console.log("Handling " + type + " for " + this.id) } };
Теперь код работает так, как и задумывалось, но выглядит более громоздко. Кроме того, вызывая bind(this) вы каждый раз создаёте новую функцию, значение this которой завязано на значении pageHandler , но зато код работает так, как вы задумывали.

Стрелочные функции решают проблему более элегантным способом, поскольку используют лексическое связывание значения this (а также super и arguments ) и его значение определяется значением this в том месте, где стрелочная функция была создана. Например:

Var pageHandler = { id: "123456" , init: function() { document.addEventListener("click", event => this.doSomething(event.type)); } , doSomething: function(type) { console.log("Handling " + type + " for " + this.id) } };
В этом примере обработчик это стрелочная функция в которой вызывается this.doSomething() . Значение this будет тем же, что и в функции init() , и код в данном примере отработает правильно, аналогично тому, который использовал bind() . Вне зависимости от того, возвращает вызов this.doSomething() значение или нет, выражение внутри тела стрелочной функции не нужно обрамлять в фигурные скобки.

Кроме того, пример выше ещё и эффективнее вызова bind() , потому что для браузера он аналогичен следующему коду:

Var pageHandler = { id: "123456" , init: function() { var self = this; document.addEventListener("click", function(event) { return self.doSomething(event.type) }); } , doSomething: function(type) { console.log("Handling " + type + " for " + this.id) } };
То есть не происходит создание новой функции, как в случае с вызовом bind() .

«Прокидывание» контекста между несколькими вызовами Очевидно, что можно вкладывать одну стрелочную функцию в другую, тем самым «прокидывая» значение this через них:

Var obj = { arr1: , arr2: ["a", "b", "c"] , concatenate: function(a, b){ return a + "|" + b } , intersection: function() { return this.arr1.reduce((sum, v1) => // arrow function 1 this.arr2.reduce((sum, v2) => { // arrow function 2 sum.push(this.concatenate(v1, v2)) return sum; } , sum) , ) } }; var arrSum = obj.intersection();//["1|a", "1|b", "1|c", "2|a", "2|b", "2|c", "3|a", "3|b", "3|c"]

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

Var result = values.sort(function(a, b) { return a - b });
Довольно многословно для простой операции. Сравните с короткой записью стрелочной функции:

Var result = values.sort((a, b) => a - b);
Использование таких методов, как массивные sort() , map() , reduce() и так далее, может быть упрощено с использованием короткого синтаксиса стрелочной функции.

Другие особенности стрелочных функций Несмотря на то, что стрелочные функции отличаются от традиционных функций, у них есть общие черты:
  • Оператор typeof вернёт "function" для стрелочной функции
  • Стрелочная функция также экземпляр «класса» Function, поэтому instanceof сработает так же как, и с традиционной функцией
  • Вы всё ещё можете использовать методы call() , apply() , и bind() , однако помните, что они не будут влиять на значение this
  • Вы можете использовать метод toMethod() , однако он не будет менять значение super (метод toMethod() введён в es6 и не рассматривается в рамках данной статьи ).
Существенным отличием от традиционных функций является то, что попытка вызвать стрелочную функцию с указанием оператора new вызовет ошибку исполнения.Итог Стрелочные функции это одно из интереснейших нововведений в ECMAScript 6, которое, имея краткий синтаксис определения, упростит передачу функций в качестве значения параметра другой функции.

Лаконичный синтаксис позволит писать сложные вещи ещё сложнее проще. Например, так будет выглядеть генератор идентификаторов (который на es5 выглядит куда многословней):

Let idGen = (start = 0, id = start, reset = (newId = start) => id = newId, next = () => id++) => ({reset, next}); let gen = idGen(100); console.log(gen.next(), gen.next(), gen.reset(10), gen.next());//100 101 10 10
А лексическое связывание закроет один из самых больших источников боли и разочарования для разработчиков, а так же улучшит производительность за счёт оптимизации на уровне js-движка.


Если вы хотите попробовать стрелочные функции, то можете выполнить вышеуказанные примеры в консоли Firefox, который на данный момент (02.2014 FF28) почти полноценно поддерживает стрелочные функции (FF28 неправильно вычисляет значение arguments ).

Также вы можете попробовать стрелочные функции и другие возможности es6 в онлайн трансляторе Traceur .

Теги: Добавить метки

О ключевом слове «this» языка JavaScript: особенности использования с пояснениями

Тайна this

Долгое время ключевое слово this оставалось для меня загадкой. Это мощный инструмент, но разобраться в нём нелегко.

С точки зрения Java, PHP или любого другого обычного языка this расценивается как экземпляр текущего объекта в методе класса, не больше и не меньше. Чаще всего его нельзя использовать вне метода, и этот подход не вызывает непонимания.

В JavaScript this - это текущий контекст исполнения функции. Поскольку функцию можно вызвать четырьмя способами:

  • вызов функции: alert("Hello World!") ,
  • вызов метода: console.log("Hello World!") ,
  • вызов конструктора: new RegExp("\\d") ,
  • непрямой вызов: alert.call(undefined, "Hello World!") ,

и каждый из них определяет свой контекст, поведение this слегка не соответствует ожиданиям начинающих разработчиков. Кроме того, strict mode также влияет на контекст исполнения.

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

Прежде чем мы начнём, давайте познакомимся с несколькими терминами:

  • Вызов - это исполнение кода тела функции. Например, вызовом функции parseInt будет parseInt("15") .
  • Контекстом вызова является значение this в теле функции.
  • Область видимости функции - это набор переменных, объектов и функций, к которым можно получить доступ из тела функции.

  • 2.1.
    2.2.
    2.3.

  • 3.1.
    3.2.

  • 4.1.
    4.2.

  • 5.1.
  • С
    6.1.

  • 7.1.
    7.2.
  • Вызов функции

    Вызов функции совершается, когда за выражением, являющимся объектом функции, следуют открывающая скобка (, разделённый запятыми список аргументов и закрывающая скобка) , например, parseInt("18") . Выражение не может быть аксессором myObject.myFunction , который совершает вызов метода. Например, .join(",") - это вызов не функции, а метода.

    Простой пример вызова функции:

    Function hello(name) { return "Hello " + name + "!"; } // Function invocation var message = hello("World"); console.log(message); // => "Hello World!"

    hello("World") - это вызов функции: hello расценивается как объект функции, за которым в скобках следует аргумент "World" .

    Var message = (function(name) { return "Hello " + name + "!"; })("World"); console.log(message); // => "Hello World!"

    Это тоже вызов функции: первая пара скобок (function(name) {...}) расценивается как объект функции, за которым в скобках следует аргумент: ("World") .

    this при вызове функции

    this - это глобальный объект при вызове функции

    Глобальный объект определяется средой исполнения. В веб-браузере это объект window .

    В вызове функции контекстом исполнения является глобальный объект. Давайте проверим контекст следующей функции:

    Function sum(a, b) { console.log(this === window); // => true this.myNumber = 20; // add "myNumber" property to global object return a + b; } // sum() is invoked as a function // this in sum() is a global object (window) console.log(sum(15, 16)); // => 31 console.log(window.myNumber); // => 20

    Когда вызывается sum(15, 16) , JavaScript автоматически инициализирует this как глобальный объект, являющийся window в браузере.

    Когда this используется вне области видимости какой-либо функции (самая внешняя область видимости: контекст глобального исполнения), он также относится к глобальному объекту:

    Console.log(this === window); // => true this.myString = "Hello World!"; console.log(window.myString); // => "Hello World!" console.log(this === window); // => true

    this при вызове функции в strict mode

    this принимает значение undefined при вызове функции в strict mode

    /* jshint esnext: true */ class City { constructor(name, traveled) { this.name = name; this.traveled = false; } travel() { this.traveled = true; } } // Constructor invocation var paris = new City("Paris", false); paris.travel();

    new City("Paris") - это вызов конструктора. Инициализация объекта управляется специальным методом класса: constructor , this которого является только что созданным объектом.

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

    Когда перед аксессором myObject.myFunction идёт ключевое слово new , JavaScript совершит вызов конструктора, а не метода. Возьмём в качестве примера new myObject.myFunction() : сперва при помощи аксессора extractedFunction = myObject.myFunction функция извлекается, а затем вызывается как конструктор для создания нового объекта: new extractedFunction() .

    this в вызове конструктора

    this - это только что созданный объект

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

    Давайте проверим контекст в следующем примере:

    Function Foo () { console.log(this instanceof Foo); // => true this.property = "Default Value"; } // Constructor invocation var fooInstance = new Foo(); console.log(fooInstance.property); // => "Default Value"

    new Foo() делает вызов конструктора с контекстом fooInstance . Объект инициализируется внутри Foo: this.property задаётся значением по умолчанию.

    Тоже самое происходит при использовании class , только инициализация происходит в методе constructor:

    /* jshint esnext: true */ class Bar { constructor() { console.log(this instanceof Bar); // => true this.property = "Default Value"; } } // Constructor invocation var barInstance = new Bar(); console.log(barInstance.property); // => "Default Value"

    Когда исполняется new Bar() , JavaScript создаёт пустой объект и делает его контекстом метода constructor . Теперь вы можете добавлять свойства, используя this: this.property = "Default Value" .

    Ловушка: как не забыть про new

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

    Var reg1 = new RegExp("\\w+"); var reg2 = RegExp("\\w+"); console.log(reg1 instanceof RegExp); // => true console.log(reg2 instanceof RegExp); // => true console.log(reg1.source === reg2.source); // => true

    При исполнении new RegExp("\\w+") и RegExp("\\w+") JavaScript создаёт эквивалентные объекты регулярных выражений.

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

    Следующий пример иллюстрирует проблему:

    Function Vehicle(type, wheelsCount) { this.type = type; this.wheelsCount = wheelsCount; return this; } // Function invocation var car = Vehicle("Car", 4); console.log(car.type); // => "Car" console.log(car.wheelsCount); // => 4 console.log(car === window); // => true

    Vehicle - это функция, задающая свойства type и wheelsCount объекту-контексту. При исполнении Vehicle("Car", 4) возвращается объект car , обладающий корректными свойствами: car.type равен "Car" а car.wheelsCount - 4 . Легко подумать, что всё работает как надо.

    Тем не менее, this - это объект window при вызове функции, и Vehicle("Car", 4) задаёт свойства объекта window - упс, что-то пошло не так. Новый объект не создан.

    Обязательно используйте оператор new , когда ожидается вызов конструктора:

    Function Vehicle(type, wheelsCount) { if (!(this instanceof Vehicle)) { throw Error("Error: Incorrect invocation"); } this.type = type; this.wheelsCount = wheelsCount; return this; } // Constructor invocation var car = new Vehicle("Car", 4); console.log(car.type); // => "Car" console.log(car.wheelsCount); // => 4 console.log(car instanceof Vehicle); // => true // Function invocation. Generates an error. var brokenCar = Vehicle("Broken Car", 3);

    new Vehicle("Car", 4) работает верно: новый объект создан и инициализирован, поскольку присутствует слово new .

    В вызове функции добавлена верификация: this instanceof Vehicle , чтобы убедиться, что у контекста исполнения верный тип объекта. Если this - не Vehicle , генерируется ошибка. Таким образом, если исполняется Vehicle("Broken Car", 3) (без new), то выбрасывается исключение: Error: Incorrect invocation .

    Непрямой вызов

    Непрямой вызов производится, когда функция вызывается методами.call() или.apply() .

    /* jshint esnext: true */ var sumArguments = (...args) => { console.log(typeof arguments); // => "undefined" return args.reduce((result, item) => result + item); }; console.log(sumArguments.name); // => "" console.log(sumArguments(5, 5, 6)); // => 16

    this в стрелочной функции

    this - это контекст, в котором определена стрелочная функция

    Стрелочная функция не создаёт свой контекст исполнения, а заимствует this из внешней функции, в которой она определена.

    Следующий пример показывает прозрачность контекста:

    /* jshint esnext: true */ class Point { constructor(x, y) { this.x = x; this.y = y; } log() { console.log(this === myPoint); setTimeout(()=> { console.log(this === myPoint); // => true console.log(this.x + ":" + this.y); // => "95:165" }, 1000); } } var myPoint = new Point(95, 165); myPoint.log();

    setTimeout вызывает стрелочную функцию в том же контексте (метод myPoint), что и метод log() . Как мы видим, стрелочная функция «наследует» контекст той функции, в которой определена.

    Если попробовать использовать в этом примере обычную функцию, она создаст свой контекст (window или undefined). Поэтому для того, чтобы код работал корректно, нужно вручную привязать контекст: setTimeout(function() {...}.bind(this)) . Это громоздко, поэтому проще использовать стрелочную функцию.

    Если стрелочная функция определена вне всех функций, её контекст - глобальный объект:

    /* jshint esnext: true */ var getContext = () => { console.log(this === window); // => true return this; }; console.log(getContext() === window); // => true

    Стрелочная функция связывается с лексическим контекстом раз и навсегда . this нельзя изменить даже при помощи метод смены контекста:

    /* jshint esnext: true */ var numbers = ; (function() { var get = () => { return this; }; console.log(this === numbers); // => true console.log(get()); // => // Use arrow function with .apply() and .call() console.log(get.call()); // => console.log(get.apply()); // => // Bind console.log(get.bind()()); // => }).call(numbers);

    Функция, вызываемая непрямым образом с использованием.call(numbers) , задаёт this значение numbers . Стрелочная функция get также получает numbers в качестве this , поскольку принимает контекст лексически. Неважно, как вызывается get , её контекстом всегда будет numbers . Непрямой вызов с другим контекстом (используя.call() или.apply()), повторное связывание (с использованием.bind()) не принесут эффекта.

    Стрелочную функцию нельзя использовать в качестве конструктора. Если вызвать new get() , JavaScript выбросит ошибку: TypeError: get is not a constructor .

    Ловушка: определение метода стрелочной функцией

    Вы можете захотеть использовать стрелочную функцию для объявления метода. Справедливо: их объявления гораздо короче по сравнению с обычным выражением: (param) => {...} вместо function(param) {..} .

    В этом примере демонстрируется определение метода format() класса Period с использованием стрелочной функции:

    /* jshint esnext: true */ function Period (hours, minutes) { this.hours = hours; this.minutes = minutes; } Period.prototype.format = () => { console.log(this === window); // =>

    Так как format - стрелочная функция, определённая в глобальном контексте, её this - это объект window . Даже если format исполняется в качестве метода объекта walkPeriod.format() , window остаётся контекстом вызова. Так происходит, потому что стрелочная функция имеет статический контекст, не изменяемый другими типами вызовов.

    this - это window , поэтому this.hours и this.minutes становятся undefined . Метод возвращает строку "undefined hours and undefined minutes" , что не является желаемым результатом.

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

    Function Period (hours, minutes) { this.hours = hours; this.minutes = minutes; } Period.prototype.format = function() { console.log(this === walkPeriod); // => true return this.hours + " hours and " + this.minutes + " minutes"; }; var walkPeriod = new Period(2, 30); console.log(walkPeriod.format());

    walkPeriod.format() - это вызов метода с контекстом walkPeriod . this.hours принимает значение 2 , а this.minutes - 30 , поэтому метод возвращает корректный результат: "2 hours and 30 minutes" .

    Заключение

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

    Откуда берется this ?

    а спрашивайте :

    Как функция вызывается?

    А в случае со стрелочной функцией спросите:

    Каков this там, где объявлена стрелочная функция?

    Такой подход к this убережет вас от лишней головной боли.

    Не путайтесь в контекстах! 🙂

    An arrow function expression is a syntactically compact alternative to a regular function expression , although without its own bindings to the this , arguments , super , or new.target keywords. Arrow function expressions are ill suited as methods, and they cannot be used as constructors.

    Syntax Basic syntax (param1, param2, …, paramN) => { statements } (param1, param2, …, paramN) => expression // equivalent to: => { return expression; } // Parentheses are optional when there"s only one parameter name: (singleParam) => { statements } singleParam => { statements } // The parameter list for a function with no parameters should be written with a pair of parentheses. () => { statements } Advanced syntax // Parenthesize the body of a function to return an object literal expression: params => ({foo: bar}) // Rest parameters and default parameters are supported (param1, param2, ...rest) => { statements } (param1 = defaultValue1, param2, …, paramN = defaultValueN) => { statements } // Destructuring within the parameter list is also supported var f = ( = , {x: c} = {x: a + b}) => a + b + c; f(); // 6 Description

    Two factors influenced the introduction of arrow functions: the need for shorter functions and the behavior of the this keyword.

    Shorter functions var elements = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ]; // This statement returns the array: elements.map (function(element) { return element.length; }); // The regular function above can be written as the arrow function below elements.map((element) => { return element.length; }); // // When there is only one parameter, we can remove the surrounding parentheses elements.map (element => { return element.length; }); // // When the only statement in an arrow function is `return`, we can remove `return` and remove // the surrounding curly brackets elements.map(element => element.length); // // In this case, because we only need the length property, we can use destructuring parameter: // Notice that the `length` corresponds to the property we want to get whereas the // obviously non-special `lengthFooBArX` is just the name of a variable which can be changed // to any valid variable name you want elements.map (({ length:lengthFooBArX }) => lengthFooBArX); // // This destructuring parameter assignment can also be written as seen below. However, note that in // this example we are not assigning `length` value to the made up property. Instead, the literal name // itself of the variable `length` is used as the property we want to retrieve from the object. elements.map (({ length }) => length); // No separate this

    Before arrow functions, every new function defined its own this value based on how the function was called:

    • A new object in the case of a constructor.
    • undefined in strict mode function calls.
    • The base object if the function was called as an "object method".

    This proved to be less than ideal with an object-oriented style of programming.

    Function Person() { // The Person() constructor defines `this` as an instance of itself. this.age = 0; setInterval(function growUp() { // In non-strict mode, the growUp() function defines `this` // as the global object (because it"s where growUp() is executed.), // which is different from the `this` // defined by the Person() constructor. this.age++; }, 1000); } var p = new Person();

    In ECMAScript 3/5, the this issue was fixable by assigning the value in this to a variable that could be closed over.

    Function Person() { var that = this; that.age = 0; setInterval(function growUp() { // The callback refers to the `that` variable of which // the value is the expected object. that.age++; }, 1000); } "use strict"; var obj = { a: 10 }; Object.defineProperty(obj, "b", { get: () => { console.log(this.a, typeof this.a, this); // undefined "undefined" Window {...} (or the global object) return this.a + 10; // represents global object "Window", therefore "this.a" returns "undefined" } });

    Use of the new operator

    Arrow functions cannot be used as constructors and will throw an error when used with new .

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

    Use of prototype property

    Arrow functions do not have a prototype property.

    Var Foo = () => {}; console.log(Foo.prototype); // undefined

    Use of the yield keyword

    The yield keyword may not be used in an arrow function"s body (except when permitted within functions further nested within it). As a consequence, arrow functions cannot be used as generators.

    Function body

    Arrow functions can have either a "concise body" or the usual "block body".

    In a concise body, only an expression is specified, which becomes the implicit return value. In a block body, you must use an explicit return statement.

    Var func = x => x * x; // concise body syntax, implied "return" var func = (x, y) => { return x + y; }; // with block body, explicit "return" needed

    Returning object literals

    Keep in mind that returning object literals using the concise body syntax params => {object:literal} will not work as expected.

    Var func = () => { foo: 1 }; // Calling func() returns undefined! var func = () => { foo: function() {} }; // SyntaxError: function statement requires a name

    This is because the code inside braces ({}) is parsed as a sequence of statements (i.e. foo is treated like a label, not a key in an object literal).

    You must wrap the object literal in parentheses:

    Var func = () => ({ foo: 1 });

    Line breaks

    An arrow function cannot contain a line break between its parameters and its arrow.

    Var func = (a, b, c) => 1; // SyntaxError: expected expression, got "=>"

    However, this can be amended by putting the line break after the arrow or using parentheses/braces as seen below to ensure that the code stays pretty and fluffy. You can also put line breaks between arguments.

    Var func = (a, b, c) => 1; var func = (a, b, c) => (1); var func = (a, b, c) => { return 1 }; var func = (a, b, c) => 1; // no SyntaxError thrown

    Parsing order

    Although the arrow in an arrow function is not an operator, arrow functions have special parsing rules that interact differently with operator precedence compared to regular functions.

    Let callback; callback = callback || function() {}; // ok callback = callback || () => {}; // SyntaxError: invalid arrow-function arguments callback = callback || (() => {}); // ok

    More examples // An empty arrow function returns undefined let empty = () => {}; (() => "foobar")(); // Returns "foobar" // (this is an Immediately Invoked Function Expression) var simple = a => a > 15 ? 15: a; simple(16); // 15 simple(10); // 10 let max = (a, b) => a > b ? a: b; // Easy array filtering, mapping, ... var arr = ; var sum = arr.reduce((a, b) => a + b); // 66 var even = arr.filter(v => v % 2 == 0); // var double = arr.map(v => v * 2); // // More concise promise chains promise.then(a => { // ... }).then(b => { // ... }); // Parameterless arrow functions that are visually easier to parse setTimeout(() => { console.log("I happen sooner"); setTimeout(() => { // deeper code console.log("I happen later"); }, 1); }, 1); Specifications Specification Status Comment
    ECMAScript 2015 (6th Edition, ECMA-262)
    Standard Initial definition.
    ECMAScript Latest Draft (ECMA-262)
    The definition of "Arrow Function Definitions" in that specification.
    Draft
    Browser compatibility

    The compatibility table on this page is generated from structured data. If you"d like to contribute to the data, please check out https://github.com/mdn/browser-compat-data and send us a pull request.

    Update compatibility data on GitHub

    Desktop Mobile Server Chrome Edge Firefox Internet Explorer Opera Safari Android webview Chrome for Android Firefox for Android Opera for Android Safari on iOS Samsung Internet Node.js Arrow functions Trailing comma in parameters
    Chrome Full support 45 Edge Full support Yes Firefox Full support 22

    Notes

    Full support 22

    Notes

    Notes Prior to Firefox 39, a line terminator (\n) was incorrectly allowed after arrow function arguments. This has been fixed to conform to the ES2015 specification and code like () \n =>
    IE No support No Opera Full support 32 Safari Full support 10 WebView Android Full support 45 Chrome Android Full support 45 Firefox Android Full support 22

    Notes

    Full support 22

    Notes

    Notes The initial implementation of arrow functions in Firefox made them automatically strict. This has been changed as of Firefox 24. The use of "use strict"; is now required. Notes Prior to Firefox 39, a line terminator (\n) was incorrectly allowed after arrow function arguments. This has been fixed to conform to the ES2015 specification and code like () \n => {} will now throw a SyntaxError in this and later versions.
    Opera Android Full support 32 Safari iOS Full support 10 Samsung Internet Android Full support 5.0 nodejs Full support Yes
    Chrome Full support 58 Edge ? Firefox Full support 52 IE No support No Opera Full support 45 Safari ? WebView Android Full support 58 Chrome Android Full support 58 Firefox Android Full support 52 Opera Android Full support 43 Safari iOS ? Samsung Internet Android Full support 7.0 nodejs Full support Yes
    Legend Full support Full support No support No support Compatibility unknown Compatibility unknown See implementation notes. See implementation notes.