由於本身的網站還沒弄好能被百度引擎搜索到,因此轉載到SF上原文連接:https://kiznaiver1998.cn/2019...javascript
以前裸面北森實習遭遇慘敗...閉包原本是本身仔仔細細看過幾回的內容也沒答好,通過本身仔細的感悟,而後看各大佬的資料,終於又有了一些新的體會(看了以前說的閉包...感受本身真是一個睿智...🐷)html
以前我一直聽標準答案是:java
函數記住並訪問其所在的詞法做用域,叫作閉包現象,而此時函數對做用域的引用叫作閉包。
下面有個經典的面試題用於理解 JavaScript 詞法做用域的特性:面試
var value = 1; function foo() { console.log(value); } function bar() { var value = 2; foo(); } bar(); //結果爲1 //由於詞法做用域,函數的做用域在函數定義就決定了,因此foo的value只能在foo定義的做用域向上查找,找到全局變量value = 1
利用這個特性,咱們在 foo 外再加一個函數。由於詞法做用域的關係,foo 的 value 從 foo 定義的做用域向上查找,就能找到 fn 的 value 爲 3.chrome
var value = 1; function fn(){ var value = 3; function foo() { console.log(value); } foo(); } function bar() { var value = 2; fn(); } bar();//3
經過 chrome 調試,咱們能夠看到運行過程大體爲這樣閉包
//bar執行上下文入棧 value聲明爲undefined value賦值爲2 //fn執行上下文入棧 value聲明爲undefined(這裏由於詞法做用域的關係,在fn的做用域是沒有value的) value賦值爲3 //能夠看到結果,fn是一個閉包,產生的引用是對value: 3的引用 Closure(fn) value: 3
因此以前我看到《實戰 ES2015》上說的閉包的原理是利用高階函數的特性(函數裏面能夠聲明函數),產生穿越做用域的引用(就是說 foo 函數能向上查找)。函數
雖然說上面已經說了,閉包的產生是由於詞法做用域,可是若是瞭解了執行上下文,才能感覺到「記住」的特性。網站
var value = 1; function fn(){ var value = 3; function foo() { console.log(value); } return foo; } function bar() { var value = 2; return fn(); } var result = bar(); result();//3
對於這個例子,這個函數的執行上下文棧會通過如下過程設計
ECStack.push(globalContext)//全局global入棧 ECStack.push(barContext)//bar被執行入棧 ECStack.push(fnContext)//fn入棧 ECStack.pop(fnContext)//fn出棧 ECStack.pop(outContext)//bar出棧 ECStack.push(result)//result入棧 ECStack.pop(result)//result出棧 ECStack.pop(globalContext)//global出棧 /* 以上過程均可以使用調試經過Scope看到 */
能夠看到 result 執行上下文入棧的時候,fn 已經出棧了,可是 result 仍然可以「記住」 fn 的 value 值。因此,經過閉包,咱們能夠在其餘的執行上下文中,訪問到函數的內部變量。調試
來源: https://www.cnblogs.com/kizna...以前一直以爲這個閉包沒啥好說的,實際...當時根本看閉包的視角不同
for (var i = 1 ; i <= 5 ; ++i) { (function(i){ setTimeout( function timer(){ console.log(i); } , i*1000); })(i); }
從如今本身的視角來看這裏呢,由於 timer 在匿名的當即執行函數中定義,因此對於詞法做用域來講,timer 可以向上訪問到匿名函數的 i。而咱們知道 setTimeout 做爲任務分發器,它的第一個參數(也是回調函數)會進入任務隊列,而當它執行的時候,即便匿名函數的執行上下文已經出棧,但它依然可以訪問 i,從而產生了閉包。