欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

談談JavaScript中的函數(shù)與閉包

 更新時間:2013年04月14日 23:08:38   作者:  
本篇文章,小編將與大家談談JavaScript中的函數(shù)與閉包,有需要的朋友可以參考一下

閉包這東西,說難也難,說不難也不難,下面我就以自己的理解來說一下閉包

一、閉包的解釋說明

對于函數(shù)式語言來說,函數(shù)可以保存內部的數(shù)據(jù)狀態(tài)。對于像C#這種編譯型命令式語言來說,由于代碼總是在代碼段中執(zhí)行,而代碼段是只讀的,因此函數(shù)中的數(shù)據(jù)只能是靜態(tài)數(shù)據(jù)。函數(shù)內部的局部變量存放在棧上,在函數(shù)執(zhí)行結束以后,所占用的棧被釋放,因此局部變量是不能保存的。

Javascript采用詞法作用域,函數(shù)的執(zhí)行依賴于變量作用域,這個作用域是在定義函數(shù)時確定的。因此Javascript中函數(shù)對象不僅保存代碼邏輯,還必須引用當前的作用域鏈。Javascript中函數(shù)內部的局部變量可以被修改,而且當再次進入到函數(shù)內部的時候,上次被修改的狀態(tài)仍然持續(xù)。這是因為因為局部變量并不保存在棧上,而是通過一個對象來保存。

決定使用哪個變量是由作用域鏈決定的,每次生成函數(shù)實例時,都會為之創(chuàng)建一個對象用來保存局部變量,并且把這個用于保存局部變量的對象加入作用域鏈中。不同函數(shù)對象可以通過作用域鏈關聯(lián)起來。Javascript中所有函數(shù)都是閉包,我們不能避免“產生”閉包。

引用一張《Javascript高級程序設計》中的圖來說明,雖然這張圖并不完全說明所有情況。圖中的activation object就是用于保存變量的對象。

 


簡而言之,在Javascript中:

閉包:函數(shù)實例保存著在執(zhí)行時所需要的變量的引用,而不會復制保存當時變量的值。(在Object C的實現(xiàn)中,我們可以選擇保存當時的值或者是引用)

作用域鏈:解析變量時查找變量所在的方式,以var作為終止符號,如果鏈上一直沒有var,則一直追溯到全局對象為止。

C#中的閉包特性是由編譯器把局部變量轉換成引用類型的對象成員實現(xiàn)的。

二、閉包的使用
 
下面通過一些具體例子來說明如何利用閉包這一特性:

1.閉包是在定義的時候產生的

function Foo(){ function A(){} function B(){} function C(){}}
我們每次執(zhí)行Foo()的時候,都有有A,B,C這三個函數(shù)實例(閉包)產生,當Foo執(zhí)行完畢,生成的實例沒有其他引用,因此會被當成垃圾隨之銷毀(不一定是馬上銷毀)。
我們來證實一下作用域鏈是在函數(shù)定義時確定的,所以這里顯示的應該是'local scope'

var scope = "global scope"; function checkscope() { var scope = "local scope"; function f() { return scope; } return f;}checkscope()()


同樣道理:

(function(){ function A(){} function B(){} function C(){}}())
上面的表達式執(zhí)行完后也會有A,B,C這三個函數(shù)實例(閉包)產生,因為這是一個立即執(zhí)行的匿名函數(shù),這三個閉包只能產生一次。生成的閉包沒有其他引用,因此會被當成垃圾隨之銷毀(不一定是馬上銷毀)。

我們之所以這么寫,目地有兩個

1.避免污染全局對象

2.避免多次產生相同的函數(shù)實例

 

對比下面兩個例子,閉包是如何保存作用域鏈的:

 function A(){} //比較省內存的寫法,創(chuàng)建對象速度快,開銷小 (function(prototype){ var name = "a"; function sayName () { alert(name); } function ChangeName() { name += "_changed" } prototype.sayName = sayName;//引用通過執(zhí)行匿名函數(shù)產生的閉包,閉包只會產生一次 prototype.changeName = ChangeName; }(A.prototype)) var a1 = new A(); var a2 = new A();
 a1.sayName(); a1.changeName(); a2.sayName();


--------------------------------------------------------------------------------

 function B(){ //原型鏈比較短的做法,找到方法的速度快,但是比較耗內存,每次new 調用構造器都有2個函數(shù)實例和1個變量產生。 var name = "b"; function sayName () { alert(name); } function changeName() { name += "_changed"; } this.sayName = sayName;//引用閉包,每次調用函數(shù)B都會產生新的閉包 this.changeName = changeName; }//如果函數(shù)調用之前帶有new關鍵字,則函數(shù)作為構造器使用。//本質上來說作為構造器和作為普通函數(shù)調用沒區(qū)別。如果直接調用B(),那么this對象會綁定到全局對象,新生成的閉包會代替舊的閉包賦給全局對象的changeName和sayName屬性上,因此舊的閉包會被當成垃圾回收。//如果作為構造器使用,new 關鍵字會生成一個新的對象(this指向這個新對象)并初始化這個新對象的sayName和changeName屬性,因此每次生成的閉包都會因為有引用而保留下來。 var b1 = new B(); b1.sayName(); b1.changeName(); b1.sayName(); var b2 = new B(); b2.sayName(); b1.sayName();


三、泄漏問題:在編譯語言中,函數(shù)體總在文件的代碼段中,并在運行期被裝入標志為可執(zhí)行的內存區(qū)。事實上我們不認為函數(shù)自身會有生命周期。我們在大多數(shù)情況下會認為“引用類型的數(shù)據(jù)結構”具有生存周期和泄漏的問題,如指針、對象等。

JavaScript中內存的泄漏本質上就是定義函數(shù)時生成的保存局部變量的對象因為存在引用而不被當成垃圾被回收。

1.存在循環(huán)引用

2.有些對象總不能銷毀,如IE6在DOM中的內存泄漏,或者在銷毀時不能通知到Javascript引擎,因此也就有些Javascript閉包總不能被銷毀。這些情況通常是發(fā)生在Javascript宿主對象和Javascript中原生對象溝通不暢導致。

相關文章

最新評論