摘要:但在作用域鏈中,外部函數的活動對象始終處于第二位,外部函數的外部函數的活動對象始終處于第三位,直至作為作用域鏈重點的全局執行環境。仍然引用中的函數在執行完畢后,其活動對象也不會被銷毀,因為匿名函數的作用域鏈仍然在引用這個活動對象。
前言:最近在細讀Javascript高級程序設計,對于我而言,中文版,書中很多地方翻譯的差強人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯誤,會非常感謝您的指出。文中絕大部分內容引用自《JavaScript高級程序設計第三版》。
閉包是指有權訪問另一個作用域中的變量的函數。
創建閉包的常見方式, 就是在一個函數內部創建另外一個函數。
function createComparisonFunction(propertyName) { return function(object1, object2) { var value1 = object1[propertyName]; // 注意, return出的匿名函數訪問了外部函數中的變量propertyname var value2 = object2[propertyName]; // 注意, return出的匿名函數訪問了外部函數中的變量propertyname if(value1 < value2) { return -1; } else if (value > value2) { return 1; } else { return 0; } } }
在這個例子中,突出的那兩行代碼是內部函數(一個匿名函數)中的代碼,這兩行代碼訪問了外部函數中的變量propertyName。
即使這個內部函數被返回了,而且是在其他地方被調用了,但它仍然可以訪問變量propertyName。
之所以還能夠訪問這個變量,是因為內部函數的作用域鏈中包含createComparisonFunction()的作用域。
當某個函數被調用時,會創建有一個執行環境(execution context)以及相應的作用域鏈。
然后,使用arguments和其他命名參數的值來初始化函數的活動對象(activation object)。
但在作用域鏈中,外部函數的活動對象始終處于第二位,外部函數的外部函數的活動對象始終處于第三位,...直至作為作用域鏈重點的全局執行環境。
在函數執行過程中,為讀取和寫入變量的值,就需要在作用鏈中查找變量。
function compare(value1, value2) { if(value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } } var result = compare(5,10);
以上代碼先定義了compare()函數,然后又在全局作用中調用了它。
當調用compare()時,會創建一個包含arguments、value1和value2的活動對象。
全局執行環境的變量對象(包含result和compare)在compare()執行環境的作用鏈中處于第二位。
function compare(value1, value2) { if(value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } } var result = compare(5,10); //作用域鏈偽代碼 /* compare[[scope]] = { global variable: { result, compare }, local variable: { arguments: [5, 10], value1 : 5, value2: 10 } } */
后臺的每個執行環境都有一個表示變量的對象——變量對象。
全局環境的變量對象始終存在,而compare()函數這樣的局部環境的變量對象,則只在函數執行的過程中存在。
在創建compare()函數時,會創建一個預先包含全局變量對象的作用域鏈,這個作用域鏈被保存在內部的[[Scope]]屬性中。
當調用compare()函數時,會為函數創建一個執行環境,然后通過復制函數的[[Scope]]屬性中的對象構建起執行環境的作用域鏈。此后,本地活動對象(也可以說是函數執行時,內部的變量)被創建并被推入執行環境作用域的前端。
對于函數的執行環境而言,其作用域鏈中包含兩個變量對象:
1. 本地活動對象。
2. 全局變量對象。
作用域鏈本質上是一個指向變量對象的指針列表,它只引用但不包含變量對象。
無論什么時候在函數中訪問一個變量時,就會從作用域鏈中搜索具有相應名字的變量。一般來講,當函數執行完畢后,局部活動對象就會被銷毀,內存中僅保存全局作用域(全局執行環境的變量對象)。
但是,閉包的情況又有所不同。
在另一個函數內部定義的函數會將包含函數(即外部函數)的活動對象添加到它的作用鏈中。
因此, 在createComparisonFunction()函數內部定義的匿名函數的作用域鏈中,實際上將會包含外部函數createComparisonFunction()的活動對象。
function createComparisonFunction(propertyName) { return function(object1, object2) { var value1 = object1[propertyName]; var value2 = object2[propertyName]; if(value1 < value2) { return -1; } else if(value1 > value2) { return 1; } else { return 0; } } } // 注意調用createComparisonFunction的返回值仍然是一個函數。 var compare = createComparisonFunction("name"); /* createComparisonFunction[[Scope]] = { global Variable: { createComparisionFunction }, local Variable: { arguments: ["name"], propertyName: "name" } } */ var result = compare({name: "Nicholas"}, {name:"Greg"}); /* compare[[Scope]] = { global Variable: { createComparisonFunction }, local Variable: { arguments: [{name: "Nicholas"}, {name:"Greg"}], value1: arguments[0][propertyName], value2: arguments[1][propertyName] } } //propertyName仍然引用createComparisonFunction中的propertyName */
createComparisonFuncion()函數在執行完畢后,其活動對象也不會被銷毀,因為匿名函數的作用域鏈仍然在引用這個活動對象。
換句話說, 當createComparisonFunction()函數返回后, 其執行環境的作用域鏈會被銷毀,但它的活動對象仍然會留在內存中;直到匿名函數被銷毀后,createComparisonFunction()的活動對象想象才會銷毀。
//創建函數 var compareNames = createComparisonFunction("name"); //調用函數 var result = compareNames({name: "Nicolas"}, { name: "Greg"}); //接觸對匿名函數的引用(以便釋放內存),當然如果你就是需要使用該變量,那么就不用釋放了,也要看情況的。 compareNames = null;
由于閉包會攜帶包含它的函數的作用域,因此會比其他函數占用更多的內存。過度使用閉包可能會導致內存占用過多,因此建議只在絕對必要時再考慮使用閉包。雖然像V8等優化后的JavaScript引擎會嘗試回收被閉包占用的內存,但是還是要慎重的使用閉包。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98765.html
摘要:深入系列第八篇,介紹理論上的閉包和實踐上的閉包,以及從作用域鏈的角度解析經典的閉包題。定義對閉包的定義為閉包是指那些能夠訪問自由變量的函數。 JavaScript深入系列第八篇,介紹理論上的閉包和實踐上的閉包,以及從作用域鏈的角度解析經典的閉包題。 定義 MDN 對閉包的定義為: 閉包是指那些能夠訪問自由變量的函數。 那什么是自由變量呢? 自由變量是指在函數中使用的,但既不是函數參數也...
摘要:閉包面試題解由于作用域鏈機制的影響,閉包只能取得內部函數的最后一個值,這引起的一個副作用就是如果內部函數在一個循環中,那么變量的值始終為最后一個值。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第二期,本周的主題是作用域閉包,今天是第8天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了...
摘要:使用上一篇文章的例子來說明下自由變量進階期深入淺出圖解作用域鏈和閉包訪問外部的今天是今天是其中既不是參數,也不是局部變量,所以是自由變量。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第二期,本周的主題是作用域閉包,今天是第7天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進階計...
摘要:為了更好的理解,在閱讀此文之前建議先閱讀上一篇進擊之詞法作用域與作用域鏈什么是閉包閉包的含義就是閉合,包起來,簡單的來說,就是一個具有封閉功能與包裹功能的結構。在中函數構成閉包。 為了更好的理解,在閱讀此文之前建議先閱讀上一篇《進擊JavaScript之詞法作用域與作用域鏈》 1.什么是閉包 閉包的含義就是閉合,包起來,簡單的來說,就是一個具有封閉功能與包裹功能的結構。所謂的閉包就是...
摘要:當面試中讓我解釋一下閉包時我懵逼了。這個解釋開始可能有點晦澀,讓我們抽絲剝繭摘下閉包的真面目。此文不詳述作用域有專門的主題闡述,不過作用域是理解閉包原理的基礎。這才是閉包的真正便利之處。閉包使用不當就會很坑。 原文鏈接 為什么深度學習JavaScript? JavaScript如今是最流行的編程語言之一。它運行在瀏覽器、服務器、移動設備、桌面應用,也可能包括冰箱。無需我舉其他再多不相干...
摘要:閉包確實是一個說爛了的概念,校招社招都會被問到,今天總結一番。先下定義,閉包是函數和該函數的詞法作用域的組合。在這個栗子里,函數以及它對變量的引用就構成了閉包。閉包和作用域對于閉包和作用域的關系,我的理解是閉包其實就是作用域的延伸。 閉包確實是一個說爛了的概念,校招社招都會被問到,今天總結一番。先下定義,閉包是函數和該函數的詞法作用域的組合。其實這個定義是比較教條的,可以直白的理解為閉...
閱讀 2571·2021-09-26 10:13
閱讀 5969·2021-09-08 10:46
閱讀 686·2019-08-30 15:53
閱讀 2957·2019-08-29 16:13
閱讀 2750·2019-08-26 12:23
閱讀 3478·2019-08-26 11:24
閱讀 1085·2019-08-23 18:09
閱讀 1028·2019-08-23 17:08