初步學習JS中的閉包

2022-07-16 12:45:12 字數 2966 閱讀 5302

js高階程式設計(3rd)中對閉包的定義就是一句話,首先閉包是乙個函式,怎樣的函式呢?有權訪問另乙個函式作用域中的變數 的函式。而建立閉包的常見方式就是在乙個函式的內部建立另乙個函式,就是巢狀函式。

閉包會涉及到的點主要有

①     作用域鏈(這個原理讓我們明白內部巢狀的函式是能夠訪問外部父函式裡定義的變數的,而對於巢狀函式來說引用了非自己作用域內定義的這些變數通常又被稱為自由變數)

②     函式執行的機制(這個又涉及到執行環境execution context ,環境棧(注意環境棧的底部永遠是全域性上下文global context),活動物件,變數物件等)。

當函式執行時,函式的環境會被推入到環境棧的頂部,理論上函式執行完畢後會將其環境彈出pop,將控制權返回給之前的執行環境,但是由於有了閉包,情況又會有所不同。

變數物件:每個執行環境都會有自己的變數物件,裡面儲存著在該執行環境中定義的變數和函式;

如果這個執行環境是函式function context,那麼則將其活動物件作為變數物件,活動物件中還包含了arguments(形參類陣列)、formal parameters(形參的值),活動物件還包含了argument 物件,該物件具有callee,length等屬性

理解了以上兩點,現在可以來說一下函式執行的整體流程了:

1、執行**,全域性執行環境

建立global . variable object

2、全域性變數的賦值 or 呼叫函式

呼叫函式時,會得到當前函式的活動物件activation object,該活動物件中包含了該函式內部變數的宣告,函式的宣告,形參

3、進入所呼叫的函式的上下文

進行該函式所在作用域上的變數的賦值及各種運算(此時的作用域包括全域性的variable object和當前函式執行環境的activation object)

4、分為三種情況

a、函式正常return或結束,該函式執行環境被彈出,回到step2繼續執行其他**;

b、若在函式中有內部函式的呼叫,執行step 3;

c、若函式返回了另乙個函式,且該函式有對自由變數的引用,則形成閉包。此時作用域鏈機制仍然有效,當前的執行環境function context不會被彈出環境棧,函式的活動物件也留在了記憶體中,不會在呼叫結束後被垃圾**機制**。回到step2繼續執行其他**;

5、所有**執行完畢,程式關閉,釋放記憶體

③     垃圾**機制

一般來說,乙個函式在執行開始的時候,會給其中定義的變數劃分記憶體空間儲存,以便後面的語句所用,等到函式執行完畢返回了,這些變數就被認為是無用的了,對應的記憶體空間也就被**了。下次再執行此函式的時候,所有變數又回到了最初狀態,重新賦值使用。

但是如果乙個函式parent內部又巢狀了另乙個函式child,而這個child函式又是可能在外部被呼叫到的,並且這個內部巢狀函式child又使用了外部函式parent中的某些變數,這個時候就形成了閉包,此時的記憶體**機制就會與前面一般情況有所不同。

在外部函式parent執行返回後 又直接呼叫了內部巢狀函式,如果按一般**情況這時候parent已經執行完畢被**了,那麼內部巢狀函式就沒法讀取已經被**的變數,所以在遇到閉包時,js解析器實際上會將內部巢狀函式本身和父級和祖先級的變數(自由變數)一起儲存起來,儲存在該閉包中。並且這些變數不會被記憶體**器**。只有當這些內部函式不可能被呼叫之後(比如被刪除了或者沒有了指標)才會銷毀這個閉包,同時那些不再被該閉包引用的變數才會在下一次記憶體**啟動時被**。

由於閉包會使得函式中的變數一直都被儲存在記憶體中,使得記憶體消耗很大,影響網頁效能。所以我們有必要在函式執行完畢後對其進行手動銷毀。

下面我們來用例子說明閉包的一些特性,閉包常見的有兩種形式--函式作為返回值,函式作為引數傳遞

看個例子:

1

function

test() 7}

8var anothertest =test();

9 anothertest();//

210 anothertest();//

3

以上可以看出閉包讓其引用的變數一直存在在記憶體中(注意雖然活動物件沒有被銷毀,其包含的變數函式依然存在在記憶體中,但是卻不能直接呼叫哦)

再來個例子:

1

var result =;

2function

test() 8}

9}10test();

11 result[0](); //

312 result[1](); //

313 result[2](); //

3

上面這段**中,本意是想讓test中的變數 i 被內部匿名函式迴圈使用並依次輸出索引0 1 2,但結果卻與預想不同。為什麼呢?因為閉包中記錄的自由變數只是對自由變數的乙個引用,也就說只能取得該變數最後狀態保留的那個值。本例中執行完for迴圈後 i 變數最後的值是3,所以所有引用 i 值的結果都將是3。

關於自由變數的取值來看兩個例子:

1

var test = 10;

2function

fun() 8}

9}10var f1 =fun();

11 f1(15);

12//

15<100

可以看到這個例子中test的取值是100,這個100來自於建立foo函式的作用域fun中,fun也是foo函式的父級函式;那麼自由變數到底是來自建立它的作用域還是其父級作用域呢?在看乙個例子:

1

var test = 10;

2 parameter = function

(num) 6};

7 (function

(fun) )(parameter);

11//

15>10

上面這個例子中,test的取值為10,而這個10是來自全域性作用域的。這證明了自由變數實際上是在建立這個函式的作用域中取值而不是其父級作用域中取值。 

JS學習 閉包

function eater push function myfood return obj var eater1 eater eater1.push banana eater1.eat 函式eat 和 push 共用乙個閉包 food立即執行函式,執行完會馬上把函式釋放,針對初始化功能的函式。va...

js 中的閉包

先理解 js 中的執行環境 閉包按中文的意思就是關上乙個包的意思。如果我們把函式的變數物件當做是乙個包的話,那這個詞很形象體現了它的作用 函式被呼叫時會建立它的執行環境,函式語句執行完後程式會自動銷毀這個函式的執行環境,但是當乙個函式中宣告了另乙個函式 子函式 並且如果存在對這個子函式引用,就會形成...

js中的閉包

閉包 英文 closure 1.閉包是乙個函式與作用域環境 即詞法環境 形成的閉包 2.閉包的理解 廣義的閉包 1.函式 2.這個函式能訪問到函式外部的狀態 也稱函式外部的變數 並不是我們平時理解的閉包 函式巢狀函式,並且內部函式通過return返回到外部,外部可以訪問內部函式的變數 總結 閉包 函...