Стрелочные функции - это новый тип функций, представленный в 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, поэтому мы просто получаем их явно из параметров.