再說閉包——鞭屍愚蠢的本身:(

由於本身的網站還沒弄好能被百度引擎搜索到,因此轉載到SF上

原文連接:https://kiznaiver1998.cn/2019...javascript

前情提要

以前裸面北森實習遭遇慘敗...閉包原本是本身仔仔細細看過幾回的內容也沒答好,通過本身仔細的感悟,而後看各大佬的資料,終於又有了一些新的體會(看了以前說的閉包...感受本身真是一個睿智...🐷)html

什麼是閉包

以前我一直聽標準答案是:java

函數記住並訪問其所在的詞法做用域,叫作閉包現象,而此時函數對做用域的引用叫作閉包。

什麼是詞法做用域

  • JavaScript 採用詞法做用域 -> 函數的做用域在函數定義的時候就決定了。
  • 固然,與之相對的還有動態做用域 -> 函數的做用域是在函數調用的時候決定了。

下面有個經典的面試題用於理解 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,從而產生了閉包。

閉包的做用

  • 函數柯里化
  • 延長變量生命週期
  • 建立模塊(實現面向對象的公有方法拋出私有變量的設計)
相關文章
相關標籤/搜索