Определение из MDN: "Замыкание — это комбинация функции и лексического окружения, в котором эта функция была объявлена. Это окружение состоит из произвольного количества локальных переменных, которые были в области действия функции во время создания замыкания."
Для того, чтобы понять на примере, сравним две функции:
const func1 = function () {
let count = 0;
function inner() {
++count;
}
inner();
return count;
}
и
const func2 = function() {
let count = 0
return function() {
return ++count
}
}
Обе функции увеличивают переменную count, однако результат их вызовов будет разным
func1() всегда будет возвращать 1
func2() возвращает нам другую функцию, поэтому
const closureFunc = func2()
closureFunc() // 1
closureFunc() // 2
Теперь мы видим, что func2 сохраняет предыдущее значение переменной count, то есть замыкает его.
Зачем это нужно, если мы можем вынести let count = 0 в глобальную область видимости и увеличивать ее с помощью более короткой функции? Дело в том, что использования глобальных переменных стоит избегать, так как их могут изменять другие сущности
Все перечисленные методы нужны для того, чтобы привязать к определенной функции некий контекст. Предположим, что у нас есть универсальная функция следующего вида:
function greetUser(){
const greeting = "Привет, " + this.userName
}
Допустим, что у нас есть много объектов с пользователями, у каждого из которых есть поле userName. Чтобы не писать метод greetUser() для каждого объекта, мы можем просто вызвать данную функцию в контексте определенного объекта.
Пример с call():
greet.call(person1, arg1)
Особенность метода в том, что он вызовется сразу в месте написания кода
Пример с bind():
greet.bind(person1, arg1)()
Отличие от call() в том, что надо дописать круглые скобки, чтобы вызвать функцию
Пример с apply():
greet.apply(person1, [arg1, arg2])
Отличается от остальных только тем, что аргументы нужно передавать в массиве
Все перечисленные выражения нужны для того, чтобы создавать переменные.
Отличие 1. let и const появились только в стандарте ES6 (2015), а изначально в языке использовалось только ключевое слово var. Однако на сегодняшний день большинство разработчиков советуют пользоваться let и const, так как использование var имеет свои особенности.
Отличие 2. Если мы объявим переменную с помощью var в глобальной области видимости, то такая переменная сразу же станет свойством глобального объекта window:
var a = 'hello'
console.log(window.a) // Hello
В некоторых случаях это может привести к ошибкам. У переменных, объявленных с помощью let и const, такого не происходит.
Отличие 3. К переменной, объявленной через var можно обратиться до того, как мы её объявляем. В таком случае значение данной переменной будет undefined, а если бы это была переменная, объявленная через let или const, то компилятор выдал бы ошибку. Это происходит потому, что переменные, объявленные через var "всплывают" (или хойстятся), то есть движок браузера сначала просматривает код и перед его выполнением инициализирует var-переменные. Место в коде, когда let или const недоступны, так как еще не объявлены называют временной мертвой зоной (TDZ - temporal dead zone). Однако этот термин не из документации, им пользуются сами разработчики.
Отличие 4. let и const обладают блочной областью видимости (если мы их объявим в цикле или в блоке if...else, то за их пределами они доступны не будут). Для var это не помеха, они могут быть ограничены только внутри функции, то есть у таких переменных нет блочной области видимости, а есть функциональная
Отличие 5. Все три типа переменных различаются особенностями переопределения их значений. Значение переменной, объявленной через let, можно переопределить, обратившись к имени переменной и присвоив новое значение:
let a = 10
a = 5
let a = 5 // SyntaxError: Identifier 'a' has already been declared
Значение переменной, объявленной через const, изменять нельзя, так как оно должно быть постоянным (константным). Исключение составляют объекты. Если мы создадим новое свойство через обращение к объекту, то объект, объявленный через const, будет изменен:
const a = {
name: 'Alex',
age: 25
}
a.isAdmin = false
console.log(a.isAdmin) // false