Привет всем, я Линь Ии, следующая статья о концепции замыканий JS, классических сценариях использования и обязательных к прочтению вопросах, давайте начнем читать 😁
карта разума
Закрытие
Прежде чем разбираться с замыканиями, давайте разберемся с проблемой области видимости верхнего уровня и освобождения памяти стека.
Концепция родительской области
- Где создается область верхнего уровня функции?Кто является областью верхнего уровня?
var a = 10
function foo(){
console.log(a)
}
function sum() {
var a = 20
foo()
}
sum()
/* 输出
10
/
функция
foo()создается глобально, поэтомуaРодительская областьwindow, выход10
мыслительные вопросы
var n = 10
function fn(){
var n =20
function f() {
n++;
console.log(n)
}
f()
return f
}
var x = fn()
x()
x()
console.log(n)
/* 输出
* 21
22
23
10
/
Просто предостережение: результаты отдельных выражений n++ и ++n одинаковы.
Идеи:
fnКакая переменная является возвращаемым значениемxЧто здесьfnВозвращаемое значение - это имя функцииfто естьfАдрес кучи памяти ,x()То есть функция выполняетсяf(), вместоfn(), вывод очевиден
- О том, как найти родительскую область
Ссылаться на:Полностью решить вопросы интервью о подъеме переменных JS
Освобождение памяти стека JS
-
Память кучи: хранит значения ссылочного типа, типы объектов представляют собой пары ключ-значение, а функции представляют собой строки кода.
-
Освобождение кучи памяти: назначьте переменную адреса пространства ссылочного типа на
null, или если нет переменных, занимающих память кучи, браузер освободит этот адрес -
Память стека: обеспечивает среду для выполнения кода и хранит примитивные значения.
-
Освобождение памяти стека. Как правило, приватная область действия функции освобождается после ее выполнения.
Однако существуют и особые случаи освобождения памяти стека: ① После выполнения функции, но в приватной области действия функции есть содержимое, которое все еще используется переменными вне стека, память стека не может быть освобождена и базовые значения в нем не выдаются. ② Память глобального стека будет освобождена только при закрытии страницы.
Что такое закрытие
Определение замыканий в JS Ninja Cheats (P90): замыкания позволяют функциям получать доступ и управлять переменными вне функции. Определение замыкания в Красной книге: замыкание — это функция, которая имеет доступ к переменным в области видимости другой функции. MDN определяет замыкания следующим образом: Замыкания — это функции, которые могут обращаться к свободным переменным. Свободные переменные здесь — это переменные в области видимости внешней функции.
Резюмируя вышеприведенные слова,Замыкание — это функция, которая имеет доступ к переменной в области видимости другой функции.
Причина закрытия
Если внутренняя функция имеет ссылку на внешнюю область видимости, это вызовет закрытие.. Из концепции области видимости верхнего уровня, представленной выше, на самом деле есть примеры замыканий.
return fявляется формой выражения.
var a = 0
function foo(){
var b =14
function fo(){
console.log(a, b)
}
fo()
}
foo()
подфункция здесь
foВ памяти есть ссылка на внешнюю область видимостиa, b, так что это производит закрытие
Где хранятся переменные закрытия
Прямое описание:Место, где хранятся переменные в замыкании, является памятью кучи.
- Если переменные в замыкании хранятся в памяти стека, то восстановление стека автоматически восстанавливает переменные в верхней части стека. Итак, если переменная в замыкании находится в стеке, после уничтожения переменной переменная в замыкании исчезнет. Таким образом, переменные, на которые ссылается замыкание, находятся вне памяти кучи.
Роль замыканий
- Защитите частные переменные функции от внешнего вмешательства. Формирует память стека, которая не уничтожается.
- Сохранить, сохранить значения в некоторых функциях. Замыкания могут сделать методы и свойства закрытыми
Классические сценарии использования замыканий
-
-
returnвернуть функцию
-
var n = 10
function fn(){
var n =20
function f() {
n++;
console.log(n)
}
return f
}
var x = fn()
x() // 21
вернуться сюда
f,f()Это закрытие, и есть ссылка на верхнюю область.
-
- функция как параметр
var a = '林一一'
function foo(){
var a = 'foo'
function fo(){
console.log(a)
}
return fo
}
function f(p){
var a = 'f'
p()
}
f(foo())
/* 输出
* foo
/
использовать возврат
foВернись,fo()Является ли закрытие,f(foo())Параметром для выполнения является функцияfo,потому чтоfo() 中的 aРодительской областью является функцияfoo(), поэтому выводfoo
-
- IIFE (самостоятельная функция)
var n = '林一一';
(function p(){
console.log(n)
})()
/* 输出
* 林一一
/
Он также производит замыкание
p(),существуетwindowЦитаты подn.
-
- назначение цикла
for(var i = 0; i<10; i++){
(function(j){
setTimeout(function(){
console.log(j)
}, 1000)
})(i)
}
Из-за наличия замыкания вышеперечисленное может выводить от 1 до 10 по очереди, а замыкание формирует 10 частных областей, которые не мешают друг другу. После того, как внешняя самовыполняющаяся функция удалена, ссылка на внешнюю область видимости отсутствует, и выходным результатом является непрерывная 10. Почему постоянно выводит 10, ведь JS однопоточный, и асинхронный код не будет выполняться первым (он будет запихнут в стек), пока не выполнится синхронный код.
i++В 10 в это время начинает выполняться асинхронный код.i=10Выход - все 10.
-
- Использование функции обратного вызова использует закрытие
window.name = '林一一'
setTimeout(function timeHandler(){
console.log(window.name);
}, 100)
-
- Троттлинг и защита от тряски
// 节流
function throttle(fn, timeout) {
let timer = null
return function (...arg) {
if(timer) return
timer = setTimeout(() => {
fn.apply(this, arg)
timer = null
}, timeout)
}
}
// 防抖
function debounce(fn, timeout){
let timer = null
return function(...arg){
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, arg)
}, timeout)
}
}
-
- Карри реализация
function curry(fn, len = fn.length) {
return _curry(fn, len)
}
function _curry(fn, len, ...arg) {
return function (...params) {
let _arg = [...arg, ...params]
if (_arg.length >= len) {
return fn.apply(this, _arg)
} else {
return _curry.call(this, fn, len, ..._arg)
}
}
}
let fn = curry(function (a, b, c, d, e) {
console.log(a + b + c + d + e)
})
fn(1, 2, 3, 4, 5) // 15
fn(1, 2)(3, 4, 5)
fn(1, 2)(3)(4)(5)
fn(1)(2)(3)(4)(5)
На что обратить внимание при использовании затворов
Это может легко привести к утечке памяти. Замыкания несут области действия, содержащие другие функции, и поэтому занимают больше памяти, чем другие функции. Чрезмерное использование замыканий может привести к чрезмерному использованию памяти, поэтому используйте замыкания с осторожностью.
Как проверить утечку памяти
- Панель производительности и панель памяти могут найти явление и место утечки
Подробности можно посмотреть:js сценарии утечки памяти, как отслеживать и анализировать
Классические вопросы для интервью
- для петель и замыканий (известный как обязательный)
var data = [];
for (var i = 0; i < 3; i++) {
data[i] = function () {
console.log(i);
};
}
data[0]();
data[1]();
data[2]()
/* 输出
3
3
3
/
здесь
iявляется глобальнымi, поделиться областью, когда функция выполняется в это времяi=3, в результате чего структура вывода будет состоять из всех 3.
- Используйте замыкания, чтобы улучшить описанный выше метод написания для достижения желаемого эффекта, способ написания 1: самовыполняющиеся функции и замыкания.
var data = [];
for (var i = 0; i < 3; i++) {
(function(j){
setTimeout( data[j] = function () {
console.log(j);
}, 0)
})(i)
}
data[0]();
data[1]();
data[2]()
- Письмо 2: использовать
let
var data = [];
for (let i = 0; i < 3; i++) {
data[i] = function () {
console.log(i);
};
}
data[0]();
data[1]();
data[2]()
letС областью блочного уровня три сформированные частные области не мешают друг другу.
Чем вопрос на размышление отличается от приведенного выше (байты)
var result = [];
var a = 3;
var total = 0;
function foo(a) {
for (var i = 0; i < 3; i++) {
result[i] = function () {
total += i * a;
console.log(total);
}
}
}
foo(1);
result[0](); // 3
result[1](); // 6
result[2](); // 9
СОВЕТ: Также формируется замыкание. Total не уничтожается внешней ссылкой.
Ссылаться на
Глубокое понимание того, что замыкания JavaScript являются замыканиями
Спасибо, что прочитали это, я Линь Ии, увидимся в следующий раз