03.11 我們常說的js和php中閉包到底是什麼

什麼是閉包

首先我想借用一下某本書中對js閉包的解釋:

書中理解:JavaScript中的函數運行在它們被定義的作用域裡,而不是它們被執行的作用域裡。

我的理解:閉包就是一個函數把外部的那些不屬於自己的對象也包含(閉合)進來了。

簡而言之:JavaScript中的閉包,無非就是變量解析的過程。

為什麼需要閉包?

局部變量無法共享和長久的保存,而全局變量可能造成變量汙染,所以我們希望有一種機制既可以長久的保存變量又不會造成全局汙染。

特點

  • 佔用更多內存

  • 不容易被釋放

何時使用?

變量既想反覆使用,又想避免全局汙染

如何使用?

1.定義外層函數,封裝被保護的局部變量。

2.定義內層函數,執行對外部函數變量的操作。

3.外層函數返回內層函數的對象,並且外層函數被調用,結果保存在一個全局的變量中。

函數生命週期

我們常說的js和php中閉包到底是什麼

函數生命週期

首先看一段話:

每次定義一個函數,都會產生一個作用域鏈(scope chain)。當JavaScript尋找變量varible時(這個過程稱為變量解析),總會優先在當前作用域鏈的第一個對象中查找屬性varible ,如果找到,則直接使用這個屬性;否則,繼續查找下一個對象的是否存在這個屬性;這個過程會持續直至找到這個屬性或者最終未找到引發錯誤為止

看個簡單版的例子:

(function(){
var hello="hello,world";
function welcome(hi){
alert(hi); //解析到作用域鏈的第一個對象的屬性
alert(hello); //解析到作用域鏈的第二個對象的屬性
}
welcome("It's easy");})();

運行結果很簡單,一個彈窗It's easy.一個彈窗hello,world。

分析過程如下:

對於函數welcome(),定義welcome的時候會產生一個作用域鏈對象,為了表示方便,記作scopechain。scopechain是個有順序的集合對象。

  • scopechain的第一個對象:為了方便表示記作sc1, sc1有若干屬性,引用本函數的參數和局部變量,如sc1.hi ;

  • scopechain的第二個對象:為了方便表示記作sc2,sc2有若干屬性,引用外層函數的參數和局部變量,如sc2.hello;

  • ...

  • scopechain的最後一個對象:為了方便表示記作scn,scn引用的全局的執行環境對象,也就是window對象!,如scn.eval();

這裡之所以可以彈出hello,world,原因就是變量解析時在welcome函數作用域鏈的第一個對象上找不到hello屬性,然後就去第二個對象上找去了(結果還真找到了)。

所以,JavaScript中的所謂的高大上的閉包其實很簡單,根本上還是變量解析。而之所以可以實現,還是因為變量解析會在作用域鏈中依次尋找對應屬性的導致的。

閉包就是一個函數引用另外一個函數的變量,因為變量被引用著所以不會被回收,因此可以用來封裝一個私有變量。這是優點也是缺點,不必要的閉包只會徒增內存消耗!另外使用閉包也要注意變量的值是否符合你的要求,因為他就像一個靜態私有變量一樣。閉包通常會跟很多東西混搭起來,接觸多了才能加深理解,這裡只是開個頭說說基礎性的東西。

總結:

第一,對象的引用,函數也是對象,首先我們要理解js面向對象的思想和原型及引用,a和b是兩個對象,b繼承了a的原型,如果查詢b.x這個屬性,可是對象b中沒有x這個屬性,js就會順著去a中找x這個屬性,這也就是閉包中內部函數訪問外部變量的原理。

第二就是js的垃圾回收機制,js的垃圾回收機制很簡單,就是當一個變量的生命週期結束就是不用了的時候就會回收,舉個例子,如果函數a中嵌套了函數b,a中聲明瞭變量x,函數b訪問函數a中的x,因為js中函數是級聯執行的,也就是我們說的鏈式執行,a執行時x進入執行狀態,如果不用了的話x就會進入執行完畢狀態,這時就會被回收,如果這時候b引用了x變量,相當於x一直處於執行狀態,垃圾回收機制就不會回收x,x變量會一直存在內存中,這種方式主要是為了保護私有變量而且可以存儲那些不持久型的動態變量。

第三,就是作用域scope,在js中每一個函數都是廣義的一個閉包,每個函數除了聲明的全局變量剩下的變量都是自己的私有變量,作用域都只限於本函數作用域內,閉包相當於暴露與外的一個接口,可以把私有變量暴露給調用者使用。

最後,閉包是js一大特性,應該合理利用閉包,濫用閉包會造成內存洩漏

我們常說的js和php中閉包到底是什麼

我們常說的js和php中閉包到底是什麼


分享到:


相關文章: