Php стрелочные функции. О ключевом слове «this» языка JavaScript: особенности использования с пояснениями. Новый синтаксис функций

Последнее обновление: 09.04.2018

Стрелочные функции (arrow functions) представляют сокращенную версию обычных функций. Стрелочные функции образуются с помощью знака стрелки (=>), перед которым в скобках идут параметры функции, а после - собственно тело функции. Например:

Let sum = (x, y) => x + y; let a = sum(4, 5); // 9 let b = sum(10, 5); // 15

В данном случае функция (x, y) => x + y осуществляет сложение двух чисел и присваивается переменной sum. Функция принимает два параметра - x и y. Ее тело составляет сложение значений этих параметров. И поскольку после стрелки фактически идет конкретное значение, которое представляет сумму чисел, то функция возвращает это значение. И мы можем через переменную sum вызвать данную функцию и получить ее результат в переменные a и b.

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

Let sum = (x, y) => console.log(x + y); sum(4, 5); // 9 sum(10, 5); // 15

В данном случае функция console.log() ничего не возвращает, и соответственно функция sum также не возвращает никакого результата.

Если функция принимает один параметр, то скобки вокруг него можно опустить:

Var square = n => n * n; console.log(square(5)); // 25 console.log(square(6)); // 36 console.log(square(-7)); // 49

Если тело функции представляет набор выражений, то они облекаются в фигурные скобки:

Var square = n => { let result = n * n; return result; } console.log(square(5)); // 25

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

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

Let user = (userName, userAge) => ({name: userName, age: userAge}); let tom = user("Tom", 34); let bob = user("Bob", 25); console.log(tom.name, tom.age); // "Tom", 34 console.log(bob.name, bob.age); // "Bob", 25

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

Если стрелочная функция не принимает никаких параметров, то ставятся пустые скобки:

Var hello = ()=> console.log("Hello World"); hello(); // Hello World hello(); // Hello World

Всем привет! В этой статье мы рассмотрим, что такое стрелочные функции в ES6 и как их использовать .

Стрелочные функции – это функции, которые записываются при помощи оператора "стрелка"(=> ).

Давайте сразу рассмотрим пример:

Let add = (x, y) => x + y;
console.log(add(5, 2));

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

Сначала, в круглых скобках мы передаем аргументы, далее ставим знак стрелочки, а потом пишем код самой функции. В нашем случае она просто принимает два числа и складывает их. По идее, это то же самое, что и function expression в ES5 . Если вы используете Babel или подобные компиляторы, то, скорее всего, они напишут что-то вроде этого:

Var add = function add(x, y) {
return x + y;
};

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

Let square = x => x*x;

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

Функция без параметров:

Let func = () => 77;

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

Let multiply = (x, y) => {
let result = x*y;
return result;
};

Если вам нужно вернуть литерал объекта, то его нужно обернуть в круглые скобки:

Let getObject = () => ({ brand: "BMW" });

Самовызывающаяся функция выглядит следующим образом.

  • 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 .

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

  • Перевод

“Толстые” стрелочные функции (=>), так же известные, как arrow функции – абсолютно новая функциональность в ECMAScript 2015 (ранее известном под именем ES6). Если верить слухам, то в ECMAScript 2015 => синтаксис стал использоваться вместо –> синтаксиса под влиянием CoffeeScript . Так же, не последнюю роль сыграла похожесть передачи контекста this.

У стрелочных функций есть две главные задачи: обеспечить более лаконичный синтаксис; обеспечить передачу лексического this с родительским scope. Давайте детально рассмотрим каждую из них!

Новый синтаксис функций
Классический синтаксис функций в JavaScript отличается ригидностью, будь это функция с одной переменной или страница с множеством функций. При каждом объявлении функци, вам необходимо писать function () {}. Потребность в более лаконичном синтаксисе функций была одной из причин, почему в свое время CoffeeScript стал очень популярен. Эта потребность особенно очевидна в случае с небольшими callback функциями. Давайте просто взглянем на цепочку Promise:
function getVerifiedToken(selector) { return getUsers(selector) .then(function (users) { return users; }) .then(verifyUser) .then(function (user, verifiedToken) { return verifiedToken; }) .catch(function (err) { log(err.stack); }); }
Вверху вы видите более или менее удобоваримый код, написанный с использованием классического синтаксиса function в JavaScript. А вот так выглядит тот же самый код, переписанный с использованием стрелочного синтаксиса:
function getVerifiedToken(selector) { return getUsers(selector) .then(users => users) .then(verifyUser) .then((user, verifiedToken) => verifiedToken) .catch(err => log(err.stack)); }
Здесь надо обратить внимание на несколько важных моментов:
  • Мы потеряли function и {}, потому что наши callback функции записываются в одну строку.
  • Мы убрали (). Теперь они не обертывают список аргументов, когда присутствует только один аргумент (остальные аргументы проходят как исключения; например, (...args) => ...).
  • Мы избавились от ключевого слова return. Убирая {}, мы позволяем однострочным стрелочным функциям провести неявный возврат (в других языках такие функции часто называют лямбда функциями).
Еще раз обратим внимание на последний пункт. Неявный возврат происходит только в случае с однострочными стрелочными функциями. Когда стрелочная функция определяется с {}, даже если она является отдельным оператором, неявный возврат не происходит.
const getVerifiedToken = selector => { return getUsers() .then(users => users) .then(verifyUser) .then((user, verifiedToken) => verifiedToken) .catch(err => log(err.stack)); }
Здесь начинается самое интересное. Так как у нашей функции есть только один оператор, мы можем убрать {}, и код будет очень похож на синтаксис CoffeeScript :
const getVerifiedToken = selector => getUsers() .then(users => users) .then(verifyUser) .then((user, verifiedToken) => verifiedToken) .catch(err => log(err.stack));
И все же код выше написан с использованием синтаксиса ES2015. (Я тоже удивился, что он прекрасно скомпилировался .) Когда мы говорим о стрелочных функциях с одним оператором, это не значит, что оператор не может занимать больше одной строки, для удобства использования.

Есть, однако, один существенный минус: убрав {} из стрелочных функций, как мы можем возвратить пустой объект? Например, тот же {}?
const emptyObject = () => {}; emptyObject(); // ?
А вот как выглядит весь код вместе:
function () { return 1; } () => { return 1; } () => 1 function (a) { return a * 2; } (a) => { return a * 2; } (a) => a * 2 a => a * 2 function (a, b) { return a * b; } (a, b) => { return a * b; } (a, b) => a * b function () { return arguments; } (...args) => args () => {} // undefined () => ({}) // {}

Лексический this
История о том, как this пытались протащить в JavaScript, уже покрылась пылью. Каждая function в JavaScript задает свой собственный контекст для this. Этот контекст, с одной стороны, очень легко обойти, а, с другой стороны, он крайне раздражает. На примере ниже вы видите код для часов, которые обновляют данные каждую секунду, обращаясь к jQuery:
$(".current-time").each(function () { setInterval(function () { $(this).text(Date.now()); }, 1000); });
При попытке сослаться на this DOM элемента, заданный через each в callback’е setInterval, мы, к сожалению, получаем совсем другой this, – тот, который принадлежит callback. Обойти этот момент можно, задав переменную that или self:
$(".current-time").each(function () { var self = this; setInterval(function () { $(self).text(Date.now()); }, 1000); });
“Толстые” стрелочные функции могут помочь решить эту проблему, так как они не имеют this:
$(".current-time").each(function () { setInterval(() => $(this).text(Date.now()), 1000); });
Как насчет аргументов?
Одним из минусов стрелочных функций является то, что у них нет собственной переменной arguments, как у обычных функций:
function log(msg) { const print = () => console.log(arguments); print(`LOG: ${msg}`); } log("hello"); // hello
Повторимся, что у стрелочных функций нет this и нет arguments. Однако, приняв это во внимание, вы все же можете получить аргументы, переданные в стрелочные функции с помощью rest-параметров (так же известны, как spread операторы):
function log(msg) { const print = (...args) => console.log(args); print(`LOG: ${msg}`); } log("hello"); // LOG: hello
Как насчет генераторов?
“Толстые” стрелочные функции не могут использоваться как генераторы. Никаких исключений и обходных путей нет. Точка.
Вывод
“Толстые” стрелочные функции – одна из причин, почему я так люблю JavaScript. Очень соблазнительно просто начать использовать => вместо function. Я видел целые библиотеки, где используется только вариант =>. Не думаю, однако, что это разумно. В конце концов, у => есть много особенностей и скрытых функций. Я рекомендую использовать стрелочные функции только там, где вам нужна новая функциональность:
  • Функции с одиночными операторами, которые сразу же делают возврат;
  • функции, которые должны работать с this с родительским scope.
ES6 сегодня
Так можно ли воспользоваться возможностями ES6 уже сегодня? Использование транспайлеров стало нормой в последние несколько лет. Ни простые разработчики, ни крупные компании не стесняются их применять.

Они стали очень модными, мы видим их во всех новых статьях. И, если вы не привыкли к ним, вам будет трудно понимать современный (на ES6) код, содержащий стрелочные функции.

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

Вот небольшой пример:

Const addOne = function(n) { return n + 1; }

Приведенный выше код можно записать так:

Const addOne = (n) => { return n + 1; }

Или, в данном случае, еще короче:

Const addOne = (n) => n + 1;

Второй пример использует { ... } фигурные скобки, но, поскольку, в нем всего одна строка кода, фигурные скобки могут быть опущены и возврат подразумевается сам собой, как видно из третьего примера.

Один параметр

Когда стрелочная функция имеет один параметр, скобки можно опустить:

// Было: someCallBack((results) => { ... }) // Стало: someCallBack(results => { ... })

Но, если нет никаких параметров, вы должны использовать открывающую и закрывающую скобки:

SomeCallBack(() => { ... })

Функции обратного вызова

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

Var _this = this; someCallBack(function() { _this.accessOuterScope(); })

Существует несколько вариаций этого «_this» (например, «self» или «that»), но идея та же. В функциях обратного вызова нам нужен доступ к версии внешней области видимости, но идея та же. В функциях обратного вызова нам нужен доступ к версии внешней области видимости this , которая теперь отличается от предыдущей, поскольку мы говорим о функции обратного вызова.

С помощью стрелочных функций , мы получаем "block scope" (блочную область видимости) и «this», которые являются одним и тем же «this» в обоих случаях. Это означает, что код сверху можно переписать без _this = this:

SomeCallBack(() => { this.accessOuterScope(); })

"Обертка"

Давайте представим ситуацию, как в React, где событие onClick должно вызывать doSomething() , (), но также должно передавать аргументы в doSomething() (например ID). Этот пример на самом деле не работает:

Некий юзер
} })

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

Const User = React.createClass(function() { render: function() { return

Некий юзер
}, onClick: function() { doSomething(this.props.userId); } })

Отсутствие круглой скобки в this.onClick означает, что это просто отсылка к функции, а не вызов ее.

Функция onClick() теперь является чем-то вроде «обертки» для doSomething() . Со стрелочными функциями можно делать «обертки» такого типа:

Const User = React.createClass(function() { render: function() { return

doSomething(this.props.userId)}>Некий юзер
} })

Как альтернативу, - мы могли бы также использовать.bind() , который не требует никаких оберток (стрелочных функций и прочего):

Const User = React.createClass(function() { render: function() { return

Некий юзер
} })

Браузерная поддрержка стрелочных функций

Если вам нужна поддержка браузеров помимо последних версий Chrome и Firefox , воспользуйтесь Babel транспайлер , чтобы конвертировать написанный вами ES6-код в ES5.