Функциональное выражение и стрелочные функции в JavaScript
Статья, в которой рассмотрим ещё один способ создания функции - посредством выражения определения. Кроме этого разберём отличие этого способа объявления функции от традиционного.
В JavaScript создавать функцию кроме классического способа (Function Declaration) можно ещё посредством:
- Function Expression;
- Arrow Function.
Создание функции с использованием синтаксиса Function Expression
Function Expression - это объявление функции, которая является частью какого-либо выражения (например, присваивания).
const имя_функции = function(параметры) { // инструкции };
Например, создание функции sum
с использованием «классического» синтаксиса (Function Declaration) и Function Expression:
// Function Declaration function sum(num1, num2) { return num1 + num2; }; // Function Expression const sum = function(num1, num2) { return num1 + num2; };
Вызов функции, созданной через Function Expression, выполняется посредством указания имени переменной, содержащей эту функцию и круглых скобок, внутрь которых при необходимости можно передать аргументы.
//вызов функции с передачей её 2 аргументов (чисел 7 и 4) sum(7, 4);
При объявлении функции посредством Function Expression дополнительно указывать имя функции после ключевого слова function
не нужно.
Его имеет смысл использовать только в том случае, если вы хотите её вызвать внутри её кода. Т.е. создать рекурсию. Вызвать эту функцию по этому имени в другом месте нельзя.
const factorial = function factorialInner(num) { if (num <= 1) { return 1; } // использование factorialInner для вызова функции return factorialInner(num - 1) * num; }; // вызовем функцию factorial(5) и выведем её результат в консоль console.log(factorial(5)); // 120 // при попытке вызвать функцию по имени factorialInner получим ошибку console.log(factorialInner(5)); // Uncaught ReferenceError: factorialInner is not defined
В этом коде функция хранится в константе factorial
. Вызвать эту функцию внутри её коде можно с помощью имени factorialInner
. Но использовать factorialInner
вне тела этой функции нельзя, для этого необходимо использовать константу factorial
.
Но вызвать функцию внутри её тела можно также с помощью свойства arguments.callee
:
const factorial = function(num) { if (num <= 1) { return 1; } return arguments.callee(num - 1) * num; };
Самовызывающаяся функция (IIFE)
Самовызывающаяся функция или IIFE - это функция, которая вызывается сразу же как только до неё дойдет интерпретатор кода.
Она используется для создания закрытой области видимости, и применяется в паттерне «модуль».
Для создания самовызывающейся функции, её необходимо обернуть в круглые скобки, а затем её вызвать, т.е. разместить ещё скобки, передав в них при необходимости аргументы.
// num1 и num2 - параметры самовызывающейся функции // 7 и 4 - аргументы самовызывающейся функции (function (num1, num2) { console.log(num1 + num2); // 11 })(7, 4);
Паттерн «модуль»:
const userInfo = (function () { // имя пользователя по умолчанию let name = 'Аноним'; // возвращаем объект, состоящий из 2 функций return { getName: function () { return name; }, setName: function (newName) { name = newName; }, }; })(); console.log(userInfo.getName()); // 'Аноним' console.log(userInfo.setName('Дима')); // 'Дима' console.log(userInfo.getName()); // 'Дима' // обратиться напрямую к переменной name нельзя, только через «публичные» методы console.log(userInfo.name); // undefined
Стрелочные функции (arrow function)
Стрелочная функция (arrow function) - это современный синтаксис для создания функций, который появился с приходом ES6 (ES 2015). Он позволяет записать её более кратко по сравнению с синтаксисом Function Expression.
Базовый синтаксис стрелочной функции:
(argument1, argument2, ... argumentN) => { // тело функции }
Например, функция возвращающая среднее арифметическое двух чисел:
// Function Expression const average = function(num1, num2) { return (num1 + num2) / 2; } // Стрелочная функция const average = (num1, num2) => { return (num1 + num2) / 2; }
В этом примере мы создали стрелочную функцию с двумя параметрами num1
и num2
, которая вычисляет выражение (num1 + num2) / 2
и возвращает его результат.
Если стрелочная функция простая, т.е. она просто вычисляет выражение как в предыдущем примере, то её можно записать ещё короче:
const average = (num1, num2) => (num1 + num2) / 2;
Пример, в котором создадим стрелочную функцию, возвращающую массив определённой длины, заполненный случайными числами от 0 до 9.
const fillArr = (numElements) => { const arr = []; for (let i = 0; i < numElements; i++) { arr.push(parseInt(Math.random() * 10)); } return arr; }; // вызов функции fillArr console.log(fillArr(5)); // [1, 4, 6, 4, 9]
При создании стрелочной функции с одним параметром круглые скобки можно не указывать:
const fillArr = numElements => { ... };
Если стрелочная функция не имеет параметров, или их два и более, то круглые скобки в этом случае нужно писать обязательно:
// () - необходимо указывать при отсутствии параметров const result = numElements = () => { console.log('Привет, мир!'); }; result(); // 'Привет, мир!'
До появления стрелочных функций каждая функция имела свой this
(контекст в котором она выполнялась).
Например, в функции-конструкторе Timer
в setInterval
контекст this
указывал не на этот объект, а на window
, т.к. данная функция является его методом:
const Timer = function () { // здесь this - это ссылка на этот объект this.counter = 0; setInterval(function () { // здесь this - это window console.log(this.counter++); }, 1000); }; const timer1 = new Timer();
Чтобы в setInterval
нам получить ссылку на этот объект, нам приходилось сохранять её в другую переменную, например that
:
const Timer = function () { this.counter = 0; // сохраняем текущий контекст в that const that = this; setInterval(function () { // используем that, которая указывает на этот объект console.log(that.counter++); }, 1000); }; const timer1 = new Timer();
Стрелочная функция не содержит собственный контекст this
. Значение this
в стрелочной функции определяется снаружи, т.е. из окружающего её контекста.
const Timer = function () { // здесь this - это ссылка на этот объект this.counter = 0; setInterval(() => { // здесь this тоже указывает на этот контекст, т.к. берётся снаружи console.log(this.counter++); }, 1000); }; const timer1 = new Timer();
Стрелочные функции не имеет собственного объекта arguments
. В этом случае получить аргументы для которых не заведены параметры можно с помощью rest параметров:
// ...otherNums - rest парамтеры const sum = (...otherNums) => { let result = 0; for (let num of otherNums) { if (typeof num === 'number') { result += num; } } return result; }; console.log(sum(2, 5, -7, 11)); // 11
Отличия между различными способами объявления функций
Отличия между Function Declaration, Function Expression и Arrow Function:
1. Вызов функции?
1.1. Function Declaration – по имени, после которого нужно указать круглые скобки:
// объявление функции square
function square(a) {
return a * a;
}
// вызов функции square
console.log(square(5)); // 25
1.2. Function Expression – по имени переменной, после которой следует указать круглые скобки:
// объявление функции square
const square = function(a) {
return a * a;
};
// вызов функции square
console.log(square(5)); // 25
1.3. Arrow Function – по имени переменной, после которой следует указать круглые скобки:
// объявление функции square
const square = a => a * a;
};
// вызов функции square
console.log(square(5)); // 25
2. Всплытие (hoisting)?
2.1. Функции Function Declaration всплывают (hoisting), их можно вызывать до объявления:
// вызываем функцию square до её объявления
console.log(square(7)); // 49
function square(a) {
return a * a;
}
2.2. Функции Function Expression нельзя использовать до объявления. Если функция сохранена в переменную, созданную с помощью ключевого слова var
, то в этом случае поднимается только сама переменная. Если сохранить функцию в const
или let
, то и переменная в данном варианте всплывать не будет.
// ошибка при вызове функции
console.log(square(7)); // Uncaught ReferenceError: Cannot access 'square' before initialization
const square = function (a) {
return a * a;
};
При сохранении функции в переменную, объявленную с помощью ключевого слова var
будет происходить следующее:
var square;
// на этом этапе переменная square имеет значение undefined
console.log(square(7)); // Uncaught TypeError: square is not a function
square = function (a) {
return a * a;
};
2.3. Поведение стрелочной функции аналогично Function Expression.
3. Видимость функции вне блока в строгом режиме ('use strict'
)?
3.1. При использовании 'use strict'
функция, объявленная как Function Declaration, будет видна только внутри блока, в котором она объявлена.
'use strict';
if (true) {
function sum(a, b, c) {
return a + b + c;
}
console.log(sum(4, 5, 4)); // 13
}
// произойдёт ошибка
console.log(sum(4, 5, 4)); // Uncaught ReferenceError: sum is not defined
3.2. В отличие от Function Declaration, доступ к функции можно получить вне блока, в котором она создана (но только, если она сохранена в переменную, созданную с помощью ключевого слова var
):
'use strict';
if (true) {
var sum = function (a, b, c) {
return a + b + c;
};
console.log(sum(10, 20, 10));
}
// имеем доступ к функции sum
console.log(sum(4, 5, 4));
3.3. Поведение стрелочной функции аналогично Function Expression.
Например:
И еще, буду благодарен если проясните: почему в данном выражении из кода выше «setTimeout(() => {this(...args);» не теряется «this», ведь используется в стрелочной функции, да и после оператора «setTimeout» свойство «this» будет «undefine»?
Спасибо за ответ.
Когда мы вызываем delay(2000) как метод First, то this будет указывать на First:
Т.е. в момент вызова delay ключевое слово this в данном коде везде будет указывать на First:
Стрелочные функции не содержат собственный контекст this и не имеют собственного объекта arguments. Они его берут снаружи.
Метод setTimeout по умолчанию выполняется в контексте объекта window, т.к. setTimeout – это сокращённая запись от window.setTimeout. Но если мы используем стрелочную функцию, то this будет браться снаружи и в данном примере нам не нужно будет привязывать необходимый нам контекст с помощью bind().
Но, если мы бы не использовали стрелочную функцию, то bind() пришлось бы использовать, иначе бы this указывал бы на объект window:
Наваял, но не работает:
Подскажите, в чем ошибка? Как можно это выражение записать стрелочной функцией?
Спасибо.
Напишите функцию, которая будет принимать на вход строку с надписью для гравировки. На выход функция отдает стоимость для гравировки. Если строка пустая или равна undefined, то цена гравировки равна 0.
Запишите в переменную строку текста, которую нужно будет выгравировать.
Создайте функцию, принимающую в качестве аргумента строку.
Воспользуйтесь методом split, чтобы получить массив слов, по аналогии с примером ниже:
Предусмотрите универсальный способ расчета стоимости гравировки от количества слов. Мы не ограничиваем клиентов по длине надписи.
Верните в качестве результата работы вычисленную стоимость.
Выведите результат работы функции в консоль в формате:
Подарочная упаковка и гравировка: