Повторяем стрелочные функции
Стрелочные функции – это не просто «сокращение», чтобы меньше писать. У них есть ряд других полезных особенностей.
При написании JavaScript-кода часто возникают ситуации, когда нам нужно написать небольшую функцию, которая будет выполнена где-то ещё.
- arr.forEach(func) – func выполняется forEach для каждого элемента массива.
- setTimeout(func) – func выполняется встроенным планировщиком.
- …и так далее.
Это очень в духе JavaScript – создать функцию и передать её куда-нибудь.
И в таких функциях мы обычно не хотим выходить из текущего контекста. Здесь как раз и полезны стрелочные функции.
У стрелочных функций нет «this»
Как мы помним из главы Методы объекта, «this», у стрелочных функций нет this . Если происходит обращение к this , его значение берётся снаружи.
Например, мы можем использовать это для итерации внутри метода объекта:
let group = < title: "Our Group", students: ["John", "Pete", "Alice"], showList() < this.students.forEach( student =>alert(this.title + ': ' + student) ); > >; group.showList();
Здесь внутри forEach использована стрелочная функция, таким образом this.title в ней будет иметь точно такое же значение, как в методе showList : group.title .
Если бы мы использовали «обычную» функцию, была бы ошибка:
let group = < title: "Our Group", students: ["John", "Pete", "Alice"], showList() < this.students.forEach(function(student) < // Error: Cannot read property 'title' of undefined alert(this.title + ': ' + student) >); > >; group.showList();
Ошибка возникает потому, что forEach по умолчанию выполняет функции с this , равным undefined , и в итоге мы пытаемся обратиться к undefined.title .
Это не влияет на стрелочные функции, потому что у них просто нет this .
Стрелочные функции нельзя использовать с new
Отсутствие this естественным образом ведёт к другому ограничению: стрелочные функции не могут быть использованы как конструкторы. Они не могут быть вызваны с new .
Стрелочные функции VS bind
Существует тонкая разница между стрелочной функцией => и обычной функцией, вызванной с .bind(this) :
- .bind(this) создаёт «связанную версию» функции.
- Стрелка => ничего не привязывает. У функции просто нет this . При получении значения this – оно, как обычная переменная, берётся из внешнего лексического окружения.
Стрелочные функции не имеют «arguments»
У стрелочных функций также нет переменной arguments .
Это отлично подходит для декораторов, когда нам нужно пробросить вызов с текущими this и arguments .
Например, defer(f, ms) принимает функцию и возвращает обёртку над ней, которая откладывает вызов на ms миллисекунд:
function defer(f, ms) < return function() < setTimeout(() =>f.apply(this, arguments), ms) >; > function sayHi(who) < alert('Hello, ' + who); >let sayHiDeferred = defer(sayHi, 2000); sayHiDeferred("John"); // выводит "Hello, John" через 2 секунды
То же самое без стрелочной функции выглядело бы так:
function defer(f, ms) < return function(. args) < let ctx = this; setTimeout(function() < return f.apply(ctx, args); >, ms); >; >
Здесь мы были вынуждены создать дополнительные переменные args и ctx , чтобы функция внутри setTimeout могла получить их.
Итого
- Не имеют this .
- Не имеют arguments .
- Не могут быть вызваны с new .
- (У них также нет super , но мы про это не говорили. Про это будет в главе Наследование классов).
Всё это потому, что они предназначены для небольшого кода, который не имеет своего «контекста», выполняясь в текущем. И они отлично справляются с этой задачей!
Как определяется this у стрелочной функции
Стрелочные функции (arrow functions) позоляют сократить определение обычных функций. Стрелочные функции определяются с помощью оператора =>, перед которым в скобках идут параметры функции, а после — собственно тело функции.
(параметры) => действия_функции
Для примера возьмем сначала обычную примитивную функцию, которая выводит сообщение на консоль:
function hello() < console.log("Hello"); >hello(); // вызываем функцию
Теперь переделаем ее в стрелочную функцию:
const hello = ()=> console.log("Hello"); hello();
В данном случае стрелочная функция присваивается константе hello , через которую затем можно вызвать данную функцию.
Здесь мы не используем параметры, поэтому указываются пустые скобки () => console.log(«Hello»);
Далее через имя переменной мы можем вызвать данную функцию.
Передача параметров
Теперь определим стрелочную функцию, которая принимает один параметр:
const print = (mes)=> console.log(mes); print("Hello Metanit.com"); print("Welcome to JavaScript");
Здесь стрелочная функция принимает один параметр mes , значение которого выводится на консоль браузера.
Если стрелочная функция имеет только один параметр, то скобки вокруг списка параметров можно опустить:
const print = mes=> console.log(mes); print("Hello Metanit.com"); print("Welcome to JavaScript");
Другой пример — передадим два параметра:
const sum = (x, y)=> console.log("Sum brush:js;"> const sum = (x, y)=> x + y; console.log(sum(1, 2)); // 3 console.log(sum(4, 3)); // 7 console.log(sum(102, 5)); // 107
Другой пример — возвратим отфарматированную строку:
const hello = name => `Hello, $`; console.log(hello("Tom")); // Hello, Tom console.log(hello("Bob")); // Hello, Bob console.log(hello("Frodo Baggins")); // Hello, Frodo Baggins
В данном случае функция hello принимает один параметр name — условное имя и создает на его основе сообщение с приветствием.
Возвращение объекта
Особо следует остановиться на случае, когда стрелочная функция возвращает объект:
const user = (userName, 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
Объект также определяется с помощью фигурных скобок, но при этом он заключается в круглые скобки.
Функция из нескольких инструкций
Выше в примерах все стрелочные функции имели только одну инструкцию. Если же функция должна выполнять больше действий, то они, как и в обычной функции, заключаются в фигурные скобки:
const square = n => < const result = n * n; console.log(result); >square(5); // 25 square(6); // 36
А если надо возвратить результат, применяется оператор return , как в обычной функции:
const square = n => < const result = n * n; return result; >console.log(square(5)); // 25
Стрелочные Функции JS — Arrow Functions (просто и с примерами)
Стрелочные функции (arrow functions) появились в 6-й редакции Javascript, более известной как ECMAScript 6. Сегодня их можно встретить практически в любом современном приложении, написанном на любом из современных JS фреймворков.
Я бы выделил 3 главных преимущества использования стрелочных функций:
- Имеют более короткий и понятный синтаксис.
- Позволяют использовать, так называемый, ‘implicit return’ (неявный) и писать функции с одной инструкцией в одну строчку
- Принимает значение this из внешней функции (не создает собственный контекст исполнения)
Давайте разберем эти преимущества более детально.
Предположим у нас есть массив данных:
1const fruits = [‘apples’, ‘oranges’, ‘bananas’, ‘kiwi’];
Давайте используем метод .map() , чтобы пробежаться по нашему массиву, добавить к каждому значению слово ‘fresh’ и получить новый массив freshFruit :
1const fruits = ['яблоки', 'апельсины', 'бананы'];23 const freshFruits = fruits.map(function (fruit)4 return `свежие $fruit>`;5 >);6 console.log(freshFruits);7 // ['свежие яблоки', 'свежие апельсины', 'свежие бананы']
При выводе результата для каждого элемента исходного массива мы использовали обратные кавычки (backticks), которые позволяют совмещать написание строчных значений и переменных.
В этом примере мы использовали обычную функцию. Теперь давайте посмотрим, как будет выглядеть наше выражение, написанное с использованием стрелочной функции.
Cтрелочные Функции JS
Для начала давайте уберем слово function и добавим символ стрелочной функции => . Таким образом мы получим выражение следующего вида:
1const fruits = ['яблоки', 'апельсины', 'бананы'];23 const freshFruits = fruits.map((fruit) =>4 return `свежие $fruit>`;5 >);6 console.log(freshFruits);7 // ['свежие яблоки', 'свежие апельсины', 'свежие бананы']
Наша функция принимает один единственный аргумент – fruit . Следовательно, мы можем избавиться от скобок вокруг него.
Также мы можем использовать так называемый implicit return (подразумеваемое возвращение…). Явное возвращение (explicit return) подразумевает использование слова return при возврате результата. Если ваша функция возвращает какой-то один результат, то написание слова return можно опустить.
В результате получаем:
1const fruits = ['яблоки', 'апельсины', 'бананы'];23 // Убрали скобки вокруг fruit и слово return4 const freshFruits = fruits.map((fruit) => `свежие $fruit>`);5 console.log(freshFruits);6 // ['свежие яблоки', 'свежие апельсины', 'свежие бананы']
Что мы изменили?
- Удалили слово return
- Записали все выражение в одну строчку
- Удалили фигурные <> скобки
После удаления слова return и фигурных скобок мы получаем implicit return, и нам не требуется уточнять, что наша функция возвращает fresh $ (это предполагается).
Что eсли стрелочная функция не принимает никаких аргументов?
В случаях, когда в функцию не требуется передавать никаких аргументов необходимо оставлять пустые скобки:
1const fruits = ['яблоки', 'апельсины', 'бананы'];23 const freshFruits = fruits.map(() => `очень свежие!`);4 console.log(freshFruits);5 // ['очень свежие!', 'очень свежие!', 'очень свежие!']
Некоторые разработчики вместо пустых скобок используют переменную _ , что имеет по большей мере эстетическое значение.
1const freshFruits = fruits.map((_) => `очень свежие!`);
Лично я предпочитаю использовать () => вместо (_) => , когда в функцию не требуется передовать аргументы.
Все стрелочные функции — безымянные
Для начала, приведу пример функции с указанием названия:
1function getFreshFruits(fruit)2 console.log(`Купим свежие $fruit>`);3 >4 getFreshFruits('бананы');
Одним из преимуществ такой функции является, то, что в случае ошибки, мы получаем название функции, где она произошла.
Стрелочные функции являются анонимными, то есть им нельзя присвоить название.
С другой стороны, стрелочную функцию можно присвоить переменной. В этом примере мы объявляем стрелочную функцию и присваиваем ее переменной, используя function expression:
1const getFfreshFruit = (fruit) =>2 console.log(`Купим свежие $fruit>`);3 >;4 getFfreshFruit('бананы');
Если, у вас нет жестких требований к более детальному отображению ошибок (с указанием названия функции), смело используйте стрелочные функции!
Значение this в стрелочных функциях
Внутри обычной функции значение this зависит от того, как была вызвана функция.
Например, при обычном выполнении функции, значение this — глобальный объект.
1function test()2 console.log(this);3 >4 test();5 // Window
Если вы вызываем функцию объекта, то значением this является сам объект.
1const testObject =2 myFunction()3 console.log(this);4 >,5 >;6testObject.myFunction();7 // testObject
У стрелочных функций отсутствует собственный контекст выполнения, следовательно, значение this в стрелочных функциях всегда наследуется от родительской (внешней) функции.
В этом примере мы получим undefined для каждого числа в массиве numbers:
1const logNumber =2 message: 'Номер > ',3 numbers: [1, 2, 3, 4],45 list()6 this.numbers.forEach(function (number)7 console.log(this.message, number);8 >);9 >,10 >;1112 logNumber();13 // undefined 114 // undefined 215 // undefined 316 // undefined 4
Значение this , которое мы получаем внутри console.log(this.message, number) определяется обычной функцией. Эта функция не привязана к нашему объекту, поэтому получаем — undefined.
Теоретически, можно использовать метод .bind() , чтобы это исправить:
1const logNumber =2 message: 'Номер > ',3 numbers: [1, 2, 3, 4],45 list()6 this.numbers.forEach(7 function (number)8 console.log(this.message, number);9 >.bind(this)10 );11 >,12 >;1314 logNumber();15 // Номер > 116 // Номер > 217 // Номер > 318 // Номер > 4
Второй вариант — использовать стрелочную функцию.
1const logNumber =2 message: 'Номер > ',3 numbers: [1, 2, 3, 4],45 list()6 // используем стрелочную функцию7 this.numbers.forEach((number) =>8 console.log(this.message, number);9 >);10 >,11 >;1213 logNumber();14 // Номер > 115 // Номер > 216 // Номер > 317 // Номер > 4
Значение this внутри обыкновенной функции — зависит от контекста вызова. Значение this в стрелочной функции всегда принимает значение внешней функции!
Замыкания в Javascript (что такое замыкание в JS + 3 примера)
Операторы Spread и Rest в Javascript ES6 (просто и c примерами)
5 отличий между обычными и стрелочными функциями
В JS ты можешь инициализировать функцию несколькими способами.
Обычно — это вариант с использованием ключевого слова function
// Function declaration (объявление функции) function greet(who) < return `Hello, $
Объявление функции и функциональное выражения я буду именовать как обыкновенная функция.
Второй путь, стал доступен в ES2015 — это стрелочный синтаксис:
const greet = (who) => < return `Hello, $
Итак, встает хороший вопрос, если у нас есть два варианта создания функции, то какой когда лучше выбрать?
Далее в статье я расскажу про различия и по окончанию ты поймешь, что лучше подходит под твои нужды.
1.1 Обыкновенные функции
Внутри обыкновенной функции значение this динамическое (в зависимости от контекста исполнения).
Динамический контекст означает, что значение this зависит от того как была вызвана функция. В JS существует 4е способа как ты можешь вызвать функцию.Во время обычного выполнения значение this эквивалентно глобальному объекту:
function myFunction() < console.log(this); >// Простое выполнение myFunction(); // контекстом будет (window)
Во время выполнения функции объекта значением this является объект, у которого был вызван метод:
const myObject = < method() < console.log(this); >>; // Вызов функции объекта myObject.method(); // контекстом будет myObject
Косвенный вызов используя myFunc.call(thisVal, arg1, . argN) или myFunc.apply(thisVal, [arg1, . argN]), значение this эквивалентно первому аргументу:
function myFunction() < console.log(this); >const myContext = < value: 'A' >; myFunction.call(myContext); // < value: 'A' >myFunction.apply(myContext); // < value: 'A' >
Вызов с помощью конструктора используя ключевое слово new, значение this эквивалентно новосозданной сущности:
function MyFunction() < console.log(this); >new MyFunction(); // MyFunction
1.2 Стрелочные функции
Поведение this внутри стрелочной функции отличается от поведения this внутри обычной функции.Не имеет значения как она была вызвана, значение this внутри стрелочной функции всегда эквивалентно значения this внешней функции. Другими словами функция не создает собственный контекст исполнения, она использует внешний.В примере выше, myMethod() это внешняя функция для стрелочной функции callback():
const myObject = < myMethod(items) < console.log(this); // myObject const callback = () =>< console.log(this); // myObject >; items.forEach(callback); > >; myObject.myMethod([1, 2, 3]);
значение this внутри стрелочной функции callback() эквивалентно значению this внешней функции myMethod().Это одна из самых крутых фишек стрелочных функций. Когда ты используешь колбек внутри метода, ты можешь быть уверен, что стрелочная функция не создаст собственный this: больше не нужны обходные пути типа const self = this или callback.bind(this).Даже если ты попытаешься вызвать стрелочную функцию через myArrowFunc.call(this) или myArrowFunc.apply(this), то ничего не изменится, у неё все так же будет this = внешнему this.
2. Конструкторы
2.1 Обыкновенные функции
Как ты мог заметить, в предыдущей секции, обычная функция может легко создавать объекты.
Например, Car() функция создаст объект автомобиля:
function Car(color) < this.color = color; >const redCar = new Car(‘red’); redCar instanceof Car; // => true
Car — это обыкновенная функция и когда мы её вызывем с помощью ключевого слова new, она создает новый объект типа Car.
2.2 Стрелочные функции
Как следствие того, что стрелочные функции не имеют собственного this они не могут быть использованы для создания объектов.Если ты попытаешься вызвать стрелочную функцию с использованием ключевого слова new, JS кинет исключение:
const Car = (color) => < this.color = color; >; const redCar = new Car(‘red’); // TypeError: Car is not a constructor
Вызов new Car(‘red’), где Car это стрелочная функция, будет сгенерирована ошибка TypeError: Car is not a constructor.
3. Объект arguments
3.1 Обыкновенные функции
Внутри тела обыкновенной функции, существует специальный массив arguments содержащий список аргументов с которым функция была вызвана.Давай вызовем функцию myFunction с двумя аргументами:
function myFunction() < console.log(arguments); >myFunction(‘a’, ‘b’); // < 0: 'a', 1: 'b'>
массив arguments будет содержать аргументы: ‘a’ и ‘b’.
3.2 Стрелочные функции
С другой стороны, в стрелочных функциях отсутствует специальное слово arguments.Опять, точно так же, как и со значение this массив arguments для стрелочных функций будет браться из внешней функции.
function myRegularFunction() < const myArrowFunction = () => < console.log(arguments); >myArrowFunction(‘c’, ‘d’); > myRegularFunction(‘a’, ‘b’); // < 0: 'a', 1: 'b' >
Стрелочная функция myArrowFunction() вызывается с аргументами ‘c’ , ‘d’. Но до сих пор, внутри тела функции, arguments такой же точно, как в функцииmy RegularFunction().
Если ты хочешь все таки получить доступ напрямую к аргументам стрелочной функции, ты можешь использовать фичу деструктуризации:
function myRegularFunction() < const myArrowFunction = (. args) => < console.log(args); >myArrowFunction(‘c’, ‘d’); > myRegularFunction(‘a’, ‘b’); // < 0: 'c', 1: 'd' >
Параметр . args собирает все аргументы преданные при вызове стрелочной функции: .
4. Неявный return
4.1 Обыкновенные функции
Только использование выражения return возвращает результат выполнения функции:
function myFunction() < return 42; >myFunction(); // => 42
Если return отсутствует внутри стрелочной функции, или после return нет выражения, функция вернет undefined:
function myEmptyFunction() < 42; >function myEmptyFunction2() < 42; return; >myEmptyFunction(); // => undefined myEmptyFunction2(); // => undefined
4.2 Стрелочные функции
Ты можешь вернуть значение из стрелочной функции, точно таким же способом, как и из обычной функции, но с одним полезным исключением.
Если стрелочная функция содержит в теле одну инструкцию, и ты опустил фигурные скобки, тогда выражение будет возвращено автоматически.
const increment = (num) => num + 1; increment(41); // => 42
Функция increment() содержит только одну инструкцию: num + 1. Это выражения неявно возвращается стрелочной функцией без использования ключевого слова return.
5.1 Обыкновенные функции
Чаще всего, обыкновенная функция используется для создания методов класса.
В классе Hero ниже, метод logName() создан с использованием синтаксиса обычной функции:
class Hero < constructor(heroName) < this.heroName = heroName; >logName() < console.log(this.heroName); >> const batman = new Hero(‘Batman’);
Иногда тебе будет нужно применить метод в качестве колбека, например для setTimeout() или для event listener`а. В таких случаях, ты можешь столкнуться с проблемой при попытке получить доступ к this.Например, давай попробуем использовать logName() метод как колбек для setTimeout():
setTimeout(batman.logName, 1000); // after 1 second logs «undefined»
По истечению 1 секунды ты увидишь в консоли undefined.setTimeout() выполняет обычный вызов функции logName (где this это глобальный объект). В данном случае метод отделен от объекта.
Давай попробуем вручную привязать контекст:
setTimeout(batman.logName.bind(batman), 1000); // after 1 second logs «Batman»
batman.logName.bind(batman) привязывает this к оъекту batman. Теперь ты можешь убедиться, что контекст не потерян.Привязка this вручную необходимое зло Но есть выход, ты можешь использовать стрелочные функции.
5.2 Стрелочные функции
Ты можешь использовать стрелочные функции как методы, внутри класса.
Сейчас, на контрасте с обыкновенной функцией, метод определенный с использованием стрелочной функции привязываетthisк объекту класса.
class Hero < constructor(heroName) < this.heroName = heroName; >logName = () => < console.log(this.heroName); >> const batman = new Hero(‘Batman’);
Сейчас ты можешь использовать batman.logName без какой-либо привязки this. Значение this внутри метода logName() всегда объект класса:
setTimeout(batman.logName, 1000); // after 1 second logs «Batman»
Понимание разница между обыкновенными и стрелочными функциями поможет сделать правильный выбор, что тебе сейчас лучше использовать.
Значение this внутри обыкновенной функции динамически зависит от контекста вызова. Собственный this внутри стрелочной функции отсутствует и она ссылается на this внешней функции. Массив arguments внутри обыкновенной функции содержит список аргументов функции. Стрелочная функция, не имеет массива arguments (но ты можешь использовать деструктуризацию, для иммитации аналога . args).Если в стрелочной функции содержится одна инструкция, то ты можешь использовать неявный return, даже без использования ключевого слова return. Последнее в списке, но не по важности — ты можешь использовать синтаксис стрелочных функций для внутри класса. При этом в качестве this будет выступать объект класса.
Уверен, что тебе было полезно, читай ещё больше статей в нашем блоге
