深入理解閉包

2021-10-02 12:19:45 字數 4136 閱讀 3420

閉包的定義

mdn 對閉包的定義:

根據上面的例子,舉乙個例子:

var str =

'xiaoqi'

;function

getname()

getname()函式可以返回str這個變數,但str即不是getname函式的區域性變數,也不是foo函式的引數,所以str就是自由變數。

這樣函式getname就是乙個閉包。

是不是覺得跟平時見到的閉包不一樣?其實,從技術的角度上看,上面的例子就是乙個閉包。

你是否要問:任何函式是否都有閉包,包括在全域性範圍內建立的函式?答案是肯定的。在全域性範圍中建立的函式也會建立乙個閉包。但由於這些函式是在全域性範圍內建立的,因此它們可以訪問全域性範圍內的所有變數,就無所謂閉包不閉包了。

實踐上的閉包

上面說的是理論上的閉包,現在說一說實踐角度的閉包:

我們通常見到的閉包:當內部函式被儲存到外部時,才會真正涉及閉包,返回的函式可以訪問自由變數。即做到下面兩點:

來看乙個例子:

var str =

'xiaoqi'

;function

check()

return getname;

}var foo =

check()

;foo()

;

分析一下它的簡要執行過程:首先說明:首先,函式定義可以儲存在變數中,函式定義在被呼叫之前對程式是不可見的; 其次每次呼叫函式時,就會臨時建立乙個本地執行上下文,當函式執行結束時,執行上下文就被銷毀。

在getname函式執行時,check函式執行上下文已經被銷毀,但因為getname儲存了check函式的作用域鏈, 所以getname函式依然可以讀到在check中定義的str(hhh)這個變數。

案例總結:上面的函式getname就是乙個實踐角度的閉包。其建立它的執行上下文銷毀之後,它依然存在,並且它訪問自由變數str(既不是getname中引數,也不是其區域性變數)。

考題題目1

var data =

;for

(var i =

0; i <

3; i++);

}data[0]

();data[1]

();data[2]();

上面提到過,函式在定義時,對程式是不可見的,當for迴圈執行之後,只會定義data[0]、data[1]、data[2]這些函式。

當執行這些函式時,才會建立執行上下文,會沿著作用域鏈向上訪問,其作用域鏈是:

scope:[ao

, globalcontext.

vo]

從全域性作用域中訪問到i。此時的i已經是3。所以結果都是3。

更改上面的例子,使用立即執行函式解決閉包問題(用閉包解決閉包):

var data =

;for

(var i =

0; i <

3; i++)}

)(i);}

data[0]

();data[1]

();data[2]();

更改之後,執行data[0]、data[1]、data[2]函式時,它們的作用域鏈:

scope:[ao

, 匿名函式context.

ao globalcontext.

vo]

所以先會去匿名函式中查詢,匿名函式的執行上下文:

匿名函式context =

, x:

0}

在data[1]、data[2]中,其匿名執行函式的執行上下文的x分別是1、2。

所以最後的結果就是0、1、2

題目2寫出下面**的執行過程,用箭頭表示其前後的兩次輸出之間有 1 秒的時間間隔,而逗號表示其前後的兩次輸出之間的時間間隔可以忽略,**實際執行的結果該如何描述?

for

(var i =

0; i <

5; i++),

1000);

}console.

log(

newdate

, i)

;

這段**考察了非同步、作用域、閉包。

題目2- - -變式1

如果想讓上述結果變為5->0,1,2,3,4,,應該如何改造**? 出現上面的結果,就是由於閉產生的問題,我們也可以使用立即執行函式解決閉包問題。

for

(var i =

0; i <

5; i++),

1000);

})(i);

}console.

log(

newdate

, i)

;

增補:有同學可能會想到es6的let ,這樣該**:

for

(var i =

0; i <

5; i++),

1000

,i);

}console.

log(

newdate

, i)

;

使用let代替var,這樣i只在本輪迴圈中有效,但是,最後console.log(new date, i);輸出的i不能訪問,let只在自己的塊級作用域中有效。**最終會報錯。

題目2 - - -變式2

如果將**輸出變成0 -> 1 -> 2 -> 3 -> 4 -> 5,並要求原有**塊中的迴圈和兩處console.log不變,應該怎麼改造**?

我們可以使用es6中promise方法。

const tasks =

;for

(var i =

0;i <

5;i++),

1000

*j)}))

}(i))}

promise.

all(tasks)

.then

(function()

,1000);

})

題目2 - - -變式3

如果要使用async/await特性來更改上面的**,該如何改?

題目3下面的輸出**是什麼:

function

fun(n,o)}}

var a=

fun(0)

;// undefined

a.fun(1

);// 0

a.fun(2

);// 0

a.fun(3

);// 0

var b=

fun(0)

.fun(1

).fun(2)

.fun(3

);// undefined 0 1 2

var c=

fun(0)

.fun(1

);// undefined 0

c.fun(2

);// 1

c.fun(3

);// 1

深入理解JavaScript閉包

一 什麼是閉包 多個 兩個或兩個以上 函式巢狀,當內部函式被儲存到外部時,將會生成閉包。內部函式在外面執行的時候一定能夠呼叫的了原來它在的那個函式環境裡的變數。閉包會導致原有作用域鏈不釋放,造成記憶體洩露。functiona var aaa 123 return b var glob 100 var...

Python 深入理解閉包

函式物件是使用def語句定義的,函式物件的作用域與def所在的層級相同。比如下面 我們在line conf函式的隸屬範圍內定義的函式line,就只能在line conf的隸屬範圍內呼叫。def line conf def line x return 2 x 1 print line 5 within...

swift深入理解閉包

我們可用swift的閉包來定義變數的值。先來乙個簡單的例子大家先感受感受。定義乙個字串的變數的方法 直接賦值 var string a string 還可以用閉包的方式定義 var string1 string 閉包還可以這麼定義,省略了等號和括號 var string2 string 閉包中可以定...