Стрелочные функции - это новый тип функций, представленный в ES2015, который предоставляет альтернативный компактный синтаксис для функций, объявляемых с помощью ключевого слова function
. У них нет собственных привязок к this
, argument
, super
или new.target
ключевым словам. Это означает, что они не подходят для методов, поскольку не связываются с this
, argument
или super
. Это также означает, что их нельзя использовать в качестве конструкторов.
Объявить стрелочные функции
Чтобы объявить стрелочные функции, мы можем написать следующее, если функция состоит из одной строки:
() => 2
Приведенная выше функция возвращает число 2. Обратите внимание, что для функций, состоящих только из одной строки, нам не нужно ключевое слово return
, чтобы что-либо возвращать. Все, что было в конце, возвращается. Также обратите внимание, что если у него нет параметров, нам нужно поставить пустые круглые скобки для сигнатуры функции. Если есть один параметр, мы можем написать что-то вроде:
x => x*2
Вышеупомянутая функция принимает параметр x
и умножает x
на 2, а затем возвращает его. Обратите внимание, что для стрелочных функций, имеющих только один параметр, нам не нужно заключать его в круглые скобки. Если у нас более 1 параметра, нам нужны круглые скобки вокруг параметров, как в следующем коде:
(x,y) => x*y
В приведенной выше функции мы берем x
и y
в качестве параметров и возвращаем x*y
. Если мы хотим вернуть объектный литерал в однострочной стрелочной функции, мы должны заключить в круглые скобки объектный литерал, который мы хотим вернуть. Например, мы пишем что-то вроде:
() => ({ a: 1, b: 2 });
вернуть объект { a: 1, b: 2 }
.
Если нам нужно определить функцию, которая охватывает несколько строк, нам нужно заключить в фигурные скобки код функции, как показано ниже:
(x, y) => { return x+y; }
Итак, общий синтаксис для стрелочных функций один из следующих:
(p1, p2, …, pN) => { statements } (p1, p2, …, pN) => expression (p) => { statements } p => { statements } () => { statements }
Параметры по умолчанию
Для параметров стрелочных функций могут быть заданы значения по умолчанию, чтобы мы могли отказаться от них при вызове функций и при этом получить для них установленное значение. Параметры по умолчанию легко сделать с помощью JavaScript. Чтобы установить их в нашем коде, мы пишем следующее:
const sum = (a,b=1) => a+b
В приведенном выше коде мы устанавливаем значение параметра по умолчанию b
равным 1, поэтому, если второй аргумент не передается при вызове функции sum
, тогда b
автоматически устанавливается в 1. Итак, если мы вызываем sum
следующим образом:
sum(1)
мы получаем 2 возвращенных.
Как мы видим, теперь нам не нужно беспокоиться о том, что необязательными аргументами будут undefined
, что было бы альтернативным результатом, если бы для параметров не было задано значение по умолчанию. Это устраняет множество источников ошибок, возникающих при использовании параметров в JavaScript как необязательные.
Альтернативный способ без использования параметров по умолчанию - проверить, является ли каждый параметр undefined
. Для этого мы пишем следующее:
const sum = (a,b,c)=>{ if (typeof b === 'undefined'){ b = 1; } if (typeof c === 'undefined'){ c = 1; } return a+b+c; }
Если мы вызовем следующую sum
функцию без каких-либо параметров, то получим:
const sum = (a,b = 1,c = 1)=> a+b+c; sum(1) // returns 3 sum(1, 2) // returns 4
Как мы видим, мы аккуратно обработали недостающие параметры с параметрами по умолчанию.
Для параметров по умолчанию передача undefined
аналогична пропуску параметров. Например, если мы вызовем следующую функцию и передадим undefined
, то получим:
const sum = (a,b = 1,c = 1)=> a+b+c; sum(1,undefined) // returns 3 sum(1,undefined,undefined) // returns 3 sum(1, 2,undefined) // returns 4
Обратите внимание, что undefined
- это то же самое, что и параметры пропуска. Это неверно для других ложных значений, которые передаются в функцию. Поэтому, если мы передадим 0, null
, false
или пустую строку, они будут переданы, а значения параметров по умолчанию будут перезаписаны. Например, если у нас есть код ниже:
const test = (num=1) => typeof num; test(undefined); test(null); test(''); test(0); test(false);
Мы получаем number
для test(undefined)
, object
для test(null)
, string
для test(string)
, number
для test(0)
и boolean
для test(false)
. Это означает, что передается все, кроме undefined
, но обратите внимание, что если мы передаем ложные значения, а затем выполняем над ними арифметические операции, то ложные значения преобразуются в 0.
const sum = (a,b = 1,c = 1)=> a+b+c; sum(1,null) sum(1,null,false) sum(1, 2,'')
Таким образом, sum(1, null)
возвращает 2, поскольку b
преобразуется в 0, а c
имеет значение по умолчанию 1. sum(1)
возвращает 1, поскольку b
и c
преобразуются в 0. sum(1, 2,’’)
равно 3, поскольку a
равно 1, b
передается так, что становится 2 вместо получение значения по умолчанию 1, а c
- это пустая строка, которая преобразуется в 0.
Аргументы по умолчанию оцениваются во время вызова, поэтому они устанавливаются каждый раз, когда они вызываются, если в параметр функции не передается аргумент со значениями по умолчанию. Например, если у нас есть:
const append = (val, arr = [])=>{ arr.push(val); return arr; }
append(1); append(2);
append(1)
возвращает [1]
, а append(2)
возвращает [2]
, поскольку мы не передавали массив при каждом вызове функции, поэтому arr
устанавливается в пустой массив при каждом запуске.
Также важно знать, что мы можем передавать значения, возвращаемые функциями в параметре по умолчанию, поэтому, если мы являемся функцией, которая что-то возвращает, мы можем вызвать ее в параметре по умолчанию и присвоить возвращаемое значение параметру. Например, мы можем написать:
const fn = () => 2 const sum(a, b = fn()) => a+b;
Затем, если мы вызовем sum(1)
, мы получим 3, поскольку функция fn
возвращает 2. Это очень удобно, если мы хотим манипулировать и комбинировать значения перед тем, как назначать их в качестве параметра.
Еще одна замечательная особенность параметров по умолчанию заключается в том, что параметры слева от данного параметра доступны для параметра для назначения в качестве значений по умолчанию, поэтому у нас может быть функция, подобная функции ниже:
const saySomething = (name, somethingToSay, message = `Hi ${name}. ${somethingToSay}`) => ({ name, somethingToSay, message });
Обратите внимание, что мы присвоили выражение параметру message
в функции saySomething
. Это отлично подходит для управления данными и последующего назначения, как мы делали раньше, путем назначения функции. Мы также можем видеть, что у нас могут быть параметры по умолчанию, которые зависят от параметров, которые находятся слева от него. Это означает, что параметры по умолчанию не должны быть статическими.
Поэтому мы вызываем его с заполненными первыми двумя параметрами, например saySomething(‘Jane’, ‘How are you doing?’)
. Мы получаем:
{name: "Jane", somethingToSay: "How are you doing?", message: "Hi Jane. How are you doing?"}
message
возвращается со строкой шаблона, которую мы определили для оценки.
Мы не можем вызывать функции, вложенные внутри функции, чтобы получить возвращаемое значение в качестве значения параметра по умолчанию. Например, если мы напишем:
const fn = (a = innerFn())=>{
const innerFn = () => { return 'abc'; }
}
Это приведет к выдаче ReferenceError
, потому что внутренняя функция еще не определена, когда задан параметр по умолчанию.
У нас также могут быть значения параметров по умолчанию, которые устанавливаются слева от обязательных параметров. Аргументы по-прежнему передаются в параметры слева направо, так что у нас есть:
const sum = (a=1,b) => a+b
Если у нас есть sum(1)
, у нас есть NaN
возврат 1 добавляется к undefined
, поскольку мы ничего не пропустили для второго аргумента, b
равно undefined
. Однако, если мы напишем sum(1,2)
, то будет возвращено 3, поскольку у нас a
установлено на 1, а b
установлено на 2.
Наконец, мы можем использовать назначение деструктуризации, чтобы установить значения параметров по умолчанию. Например, мы можем написать:
const sum = ([a,b] = [1,2], {c:c} = {c:3}) => a+b+c;
Затем мы вызываем sum
без аргументов и получаем 6, так как a
установлен в 1, b
установлен в 2, а c
установлен в 3 функцией деструктурирования присваивания, предоставляемой JavaScript.
Этот объект
Если на this
есть ссылка в обычной функции, объявленной с ключевым словом function
, объект this
не устанавливается внутри стрелочной функции для функции, имеющей this
внутри. Если стрелочная функция находится внутри конструктора, тогда this
устанавливается на новый объект. Если он не находится внутри какого-либо объекта, тогда this
внутри стрелочной функции undefined
в строгом режиме. this
будет установлен на базовый объект, если стрелочная функция находится внутри объекта. Однако мы всегда получаем window
object, если ссылаемся на this
в стрелочной функции. Например, если мы регистрируем this
внутри стрелочной функции, которая не находится внутри конструктора или класса, как в следующем коде:
const fn = () => this console.log(fn);
Мы регистрируем объект window
при запуске console.log
. Аналогичным образом, если мы запустили:
let obj = {} obj.f = () => { return this; }; console.log(obj.f());
Получаем то же, что и раньше. Это контрастирует с традиционными функциями, объявленными с ключевым словом function
. Если мы заменим приведенные выше функции традиционными функциями в приведенном выше коде, как в следующем коде:
const fn = function() { return this }; console.log(fn);
Мы получаем функцию fn
, которую мы объявили зарегистрированной для fn
.
Однако, если стрелочная функция находится внутри конструктора, мы получаем объект, свойством которого она является, как значение this
. Например, если у нас есть:
const obj = function() { this.fn = () => this; } console.log(new obj().fn())
Тогда this
будет текущим объектом, в котором находится функция this.fn
, то есть obj
, поэтому obj
будет зарегистрирован console.log(new obj().fn())
. Также, если у нас есть код ниже:
let obj = {} obj.f = function() { return this; }; console.log(obj.f());
При запуске объект obj
регистрируется.
Когда стрелочные функции вызываются с call
и apply
, аргумент для объекта this
будет проигнорирован. Это означает, что их нельзя использовать для изменения объекта this
. Например, если у нас есть:
function add(a) { return this.base + a; } console.log(add.call({ base: 1 }, 2));
Получаем 3 логина. Это означает, что мы можем передать объект, который нам нужен для this
, и получить его значение с помощью традиционных функций. Это не будет работать со стрелочными функциями. Если у нас есть:
const add = (a) => { return this.base + a; } console.log(add.call({ base: 1 }, 2));
Мы получаем NaN
, потому что this
- это window
, у которого нет свойства base
. Первый аргумент для call
игнорируется. Точно так же, если мы попытаемся установить значение this
внутри функции, когда мы используем apply
, мы можем сделать это с помощью традиционных функций:
function add(a) { return this.base + a; } console.log(add.apply({ base: 1 }, [1]));
Мы получаем 2 зарегистрированных, поскольку первый аргумент для apply
- это this
object для функции внутри. Это означает, что мы можем изменить this.base
на 1, передав наш собственный объект в качестве первого аргумента apply
. Когда мы пробуем то же самое со стрелочными функциями, как в следующем коде:
const add = (a) => { return this.base + a; } console.log(add.apply({ base: 1 }, [1]));
Мы получаем NaN
, потому что this.base
это undefined
. Снова this
устанавливается на объект window
, поскольку у нас есть стрелочная функция, поэтому this
нельзя изменить с помощью функции apply
.
Объект arguments
не привязан к стрелочной функции, поэтому мы не можем использовать объект arguments
для получения аргументов, переданных в вызов функции стрелочной функции. Если у нас есть традиционный вариант, мы можем получить аргументы, переданные с помощью объекта arguments
, как в следующем коде:
function add(){ return [...arguments].reduce((a,b) => +a + +b, []) } console.log(add(1,2,3));
Получаем 6 авторизованных в последней строке. Это потому, что объект arguments
имел все аргументы объекта в строковой форме. Таким образом, в arguments
будут перечислены '1'
, '2'
и '3'
. Однако, если мы попытаемся сделать это с помощью стрелочной функции, мы не сможем получить сумму аргументов с помощью объекта arguments
. Например, если у нас есть:
const add = () => [...arguments].reduce((a,b) => +a + +b, []) console.log(add(1,2,3));
Мы получаем NaN
, потому что не получили аргументы вызова функции в объекте arguments
.
Стрелочные функции нельзя использовать в качестве конструкторов, поэтому мы не можем создать их экземпляр с помощью ключевого словаnew
. Например, если мы напишем следующее и запустим его:
const Obj = () => {};
let obj = new Obj();
Мы получили бы TypeError
.
Кроме того, стрелочные функции не имеют свойства prototype
, поэтому, когда мы регистрируем что-то вроде:
const Obj = () => {};
console.log(Obj.prototype);
Получаем undefined
. Это означает, что мы не можем наследовать от стрелочной функции. Ключевое слово yield
нельзя использовать в теле функции стрелки. Следовательно, мы не можем использовать стрелочные функции в качестве функций генератора.
Стрелочные функции полезны в тех случаях, когда нам нужно написать функцию более кратко, чем писать традиционные функции, и нам не нужно изменять содержимое объекта this
внутри функции. Кроме того, мы не можем использовать их для функций генератора или конструктора, поскольку у них нет собственного this
объекта. У них также нет свойства prototype
, поэтому они не могут быть унаследованы от. Кроме того, аргументы вызова функции стрелки не могут быть получены из объекта arguments
, поэтому мы просто получаем их явно из параметров.