Стрелочные функции

Сводка

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

Синтаксис

Базовый синтаксис

(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
// эквивалентно: (param1, param2, …, paramN) => { return expression; }

// Круглые скобки не обязательны для единственного параметра:
(singleParam) => { statements }
singleParam => { statements }

// Функция без параметров нуждается в круглых скобках:
() => { statements }
() => expression 
// Эквивалентно: () => { return expression; }

Расширенный синтаксис

// Parenthesize the body 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 = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f();  // 6

Подробные примеры синтаксиса можно посмотреть здесь.

Описание

Смотрите также "ES6 In Depth: Arrow functions" on hacks.mozilla.org.

Два фактора повлияли на появление стрелочных функции: более короткий синтаксис и лексика this.

Короткие функции

В некоторых функциональных шаблонах приветствуются более короткие функции. Сравните:

var materials = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

var materialsLength1 = materials.map(function(material) {
  return material.length;
}); //8,6,7,9

var materialsLength2 = materials.map((material) => {
  return material.length;
}); //8,6,7,9

var materialsLength3 = materials.map(material => material.length); //8,6,7,9

 

Отсутствие связывания с this

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

function Person() {
  // В конструкторе Person() `this` указывает на себя.
  this.age = 0;

  setInterval(function growUp() {
    // В нестрогом режиме, в функции growUp() `this` указывает 
    // на глобальный объект, который отличается от `this`,
    // определяемом в конструкторе Person().
    this.age++;
  }, 1000);
}

var p = new Person();

В ECMAScript 3/5, данная проблема решалась присваиванием значения this переменной:

function Person() {
  var that = this;
  that.age = 0;

  setInterval(function growUp() {
    // Функция с обратным вызовом(callback) содержит переменную that, которая 
    // ссылается на требуемый объект this.
    that.age++;
  }, 1000);
}

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

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

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // `this` указывает на объект Person
  }, 1000);
}

var p = new Person();

Строгий режим исполнения

Поскольку значение this определяется лексикой, правила строгого режима (strict mode) относительно this игнорируются:

var f = () => {'use strict'; return this};
f() === window; // или глобальный объект

Все остальные правила строго режима применяются как обычно.

Вызов с помощью call или apply

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

var adder = {
  base : 1,
    
  add : function(a) {
    var f = v => v + this.base;
    return f(a);
  },

  addThruCall: function(a) {
    var f = v => v + this.base;
    var b = {
      base : 2
    };
            
    return f.call(b, a);
  }
};

console.log(adder.add(1));         // Выводит 2
console.log(adder.addThruCall(1)); // Всё равно выводит 2

Не имеет собственного объекта arguments

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

var arguments = 42;
var arr = () => arguments;

arr(); // 42

function foo() {
  var f = (i) => arguments[0]+i; // Неявное связание ссылки arguments 
                                 // стрелочной функции f
                                 // c объектом arguments функции foo
  return f(2);
}

foo(1); // 3

В большинстве случаев лучшей заменой объекта arguments в стрелочных функциях являются rest параметры:

function foo() { 
  var f = (...args) => args[0]; 
  return f(2); 
}

foo(1); // 2

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

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

'use strict';
var obj = {
  i: 10,
  b: () => console.log(this.i, this),
  c: function() {
    console.log(this.i, this);
  }
}
obj.b(); // prints undefined, Object {...}
obj.c(); // prints 10, Object {...}

Стрелочные функции не объявляют привязку ("bind") их контекста this. Другой пример включает Object.defineProperty():

'use strict';
var obj = {
  a: 10
};

Object.defineProperty(obj, 'b', {
  get: () => {
    console.log(this.a, typeof this.a, this);
    return this.a + 10; 
    // представляет глобальный объект 'Window', но 'this.a' возвращает 'undefined'
  }
});

 

Использование оператора new

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

var a = new (function() {}) 
// переменной a будет присвоено значение экземпляра анонимной функции

var b = new (() => {})
// будет выброшено исключениe
// Uncaught TypeError: (intermediate value) is not a constructor

Использование ключевого слова yield

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

Тело функции

Тело стрелочной функции может иметь краткую (concise body) или блочную (block body) форму.

Блочная форма не возвращает значение, необходимо явно вернуть значение.

var func = x => x * x;                  // краткий синтаксис, 
                                        // неявно возвращает результат
var func = (x, y) => { return x + y; }; // блочный синтаксис, 
                                        // явно возвращает результат

Возвращаемые объектные строки(литералы)

Помните о том, что возвращаемые объектные строки используют сокращённый синтаксис:

 params => {object:literal} будет работать не так, как ожидается.

var func = () => { foo: 1 };               
// Вызов func() возвращает undefined!

var func = () => { foo: function() {} };   
// SyntaxError: function statement requires a name

Это происходит потому что код в скобках ({}) распознаётся как цепочка выражений (т.е. foo трактуется как наименование, а не как ключ в объектной строке).

Помните оборачивать скобками объектные строки.

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

Разрывы строк

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

var func = ()
           => 1; 
// SyntaxError: expected expression, got '=>'

Разбор порядка следования

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

let callback;

callback = callback || function() {}; // ok

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

callback = callback || (() => {});    // ok

 

Больше примеров

// Пустая стрелочная функция возвращает undefined
let empty = () => {};

(() => 'foobar')(); 
// Вернёт "foobar"
// (Это Immediately Invoked Function Expression 
// смотри 'IIFE' в справочнике)

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 = [5, 6, 13, 0, 1, 18, 23];

var sum = arr.reduce((a, b) => a + b);  
// 66

var even = arr.filter(v => v % 2 == 0); 
// [6, 0, 18]

var double = arr.map(v => v * 2);       
// [10, 12, 26, 0, 2, 36, 46]

// More concise promise chains
promise.then(a => {
  // ...
}).then(b => {
   // ...
});

// Стрелочные функции без параметров, которые визуально легче разбирать
setTimeout( () => {
  console.log('I happen sooner');
  setTimeout( () => {
    // deeper code
    console.log('I happen later');
  }, 1);
}, 1);

Спецификации

Спецификация Статус Комментарий
ECMAScript 2015 (6th Edition, ECMA-262)
Определение 'Arrow Function Definitions' в этой спецификации.
Стандарт Изначальное определение.

Совместимость с браузерами

Возможность Chrome Firefox (Gecko) Internet Explorer Opera Safari
Базовая поддержка 45.0 22.0 (22.0) Нет Нет Нет
Возможность Android Chrome for Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Базовая поддержка Нет 45.0 22.0 (22.0) Нет Нет Нет

Замечания для Firefox

  • Первоначальная реализация стрелочных функций в Firefox автоматически переводила их в строгий режим. Это поведение было изменено в Firefox 24. Использование "use strict"; стало обязательным.
  • Стрелочные функции семантически отличаются от нестандартных Expression Closures, добавленных в Firefox 3 (подробности в Javascript 1.8); в Expression Closures значение this не привязано лексически.
  • До Firefox 39, перенос строки (\n) был ошибочно разрешён после аргументов стрелочной функции. Это было исправлено для соблюдения спецификации ES2015, и код вроде: () \n => {} теперь вызывает SyntaxError в этой и более поздних версиях.

See also

Метки документа и участники

 Внесли вклад в эту страницу: TotalAMD, bskydive, seriouslyfluffy, dst, vitya-ne, theViktor1990, uleming, kutase123, pashutk, dtretyakov
 Обновлялась последний раз: TotalAMD,