JavaScript 閉包
JavaScript 閉包
什麼是閉包
閉包,官方對(duì)閉包的解釋是:一個(gè)擁有許多變數(shù)和綁定了這些變數(shù)的環(huán)境的表達(dá)式(通常是一個(gè)函數(shù)),因而這些變數(shù)也是該表達(dá)式的一部分。閉包的特點(diǎn):
1. 作為一個(gè)函數(shù)變數(shù)的一個(gè)引用,當(dāng)函數(shù)傳回時(shí),其處於激活狀態(tài)。
2. 一個(gè)閉包就是當(dāng)一個(gè)函數(shù)回傳時(shí),一個(gè)沒有釋放資源的堆疊區(qū)。
簡單的說,Javascript允許使用內(nèi)部函數(shù)---即函數(shù)定義和函數(shù)表達(dá)式位於另一個(gè)函數(shù)的函數(shù)體內(nèi)。而且,這些內(nèi)部函數(shù)可以存取它們所在的外部函數(shù)中聲明的所有局部變數(shù)、參數(shù)和聲明的其他內(nèi)部函數(shù)。當(dāng)其中一個(gè)這樣的內(nèi)部函數(shù)在包含它們的外部函數(shù)之外被呼叫時(shí),就會(huì)形成閉包。
2、閉包的幾種寫法和用法
首先要明白,在JS中一切都是對(duì)象,函數(shù)是對(duì)象的一種。下面先來看看閉包的5種寫法,簡單理解一下什麼是閉包。後面會(huì)具體解釋。
//第1種寫法 ?
function Circle(r) { this.r = r; } Circle.PI = 3.14159; Circle.prototype.area = function() { return Circle.PI * this.r * this.r; } var c = new Circle(1.0); alert(c.area());
這種寫法沒什麼特別的,只是為函數(shù)增加一些屬性。
//第2種寫法 ?
var Circle = function() { var obj = new Object(); obj.PI = 3.14159; obj.area = function( r ) { return this.PI * r * r; } return obj; } var c = new Circle(); alert( c.area( 1.0 ) );
這種寫法是宣告一個(gè)變量,將一個(gè)函數(shù)當(dāng)作值賦給變數(shù)。
//第3種寫法 ?
var Circle = new Object(); Circle.PI = 3.14159; Circle.Area = function( r ) { return this.PI * r * r; } alert( Circle.Area( 1.0 ) );
這種方法最好理解,就是new 一個(gè)對(duì)象,然後給對(duì)象添加屬性和方法。
//第4種寫法 ?
var Circle={ "PI":3.14159, "area":function(r){ return this.PI * r * r; } }; alert( Circle.area(1.0) );
這種方法使用較多,也最為方便。 var obj = {}就是宣告一個(gè)空的物件。
//第5種寫法?
var Circle = new Function("this.PI = 3.14159;this.area = function( r ) {return r*r*this.PI;} "); ???
alert( (new Circle()).area(1.0) ); ?
說實(shí)話,這種寫法我是沒用過,大家可以參考一下。 ?
總的來說,上面幾種方法,第2中和第4中較為常見,大家可以依照習(xí)慣選擇。 ?
上面程式碼中出現(xiàn)了JS中常用的Prototype,那麼Prototype有什麼用呢?下面我們來看看:
var dom = function(){ }; dom.Show = function(){ alert("Show Message"); }; dom.prototype.Display = function(){ alert("Property Message"); }; dom.Display(); //error dom.Show(); var d = new dom(); d.Display(); d.Show(); //error
變數(shù)生命週期
全域變數(shù)的作用域是全域性的,也就是在整個(gè)JavaScript程式中,全域變數(shù)處處都在。
而在函數(shù)內(nèi)部宣告的變量,只在函數(shù)內(nèi)部運(yùn)作。這些變數(shù)是局部變量,作用域是局部性的;函數(shù)的參數(shù)也是局部性的,只在函數(shù)內(nèi)部起作用。
計(jì)數(shù)器困境
設(shè)想下如果你想統(tǒng)計(jì)一些數(shù)值,且該計(jì)數(shù)器在所有函數(shù)中都是可用的。
你可以使用全域變量,函數(shù)設(shè)定計(jì)數(shù)器遞增:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <p>全局變量計(jì)數(shù)。</p> <button type="button" onclick="myFunction()">計(jì)數(shù)!</button> <p id="demo">0</p> <script> var counter = 0; function add() { return counter += 1; } function myFunction(){ document.getElementById("demo").innerHTML = add(); } </script> </body> </html>
計(jì)數(shù)器數(shù)值在執(zhí)行 add() 函數(shù)時(shí)會(huì)改變。
但問題來了,頁面上的任何腳本都能改變計(jì)數(shù)器,即使沒有呼叫 add() 函數(shù)。
如果我在函數(shù)內(nèi)宣告計(jì)數(shù)器,如果沒有呼叫函數(shù)將無法修改計(jì)數(shù)器的值:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <p>局部變量計(jì)數(shù)。</p> <button type="button" onclick="myFunction()">計(jì)數(shù)!</button> <p id="demo">0</p> <script> function add() { var counter = 0; return counter += 1; } function myFunction(){ document.getElementById("demo").innerHTML = add(); } </script> </body> </html>
以上程式碼將無法正確輸出,每次我呼叫add() 函數(shù),計(jì)數(shù)器都會(huì)設(shè)定為1。
JavaScript 內(nèi)嵌函數(shù)可以解決這個(gè)問題。
JavaScript 內(nèi)嵌函數(shù)
所有函數(shù)都能存取全域變數(shù)。??
實(shí)際上,在 JavaScript 中,所有函數(shù)都能存取它們上一層的作用域。
JavaScript 支援巢狀函數(shù)。巢狀函數(shù)可以存取上一層的函數(shù)變數(shù)。
該實(shí)例中,內(nèi)嵌函數(shù)?plus()?可以存取父函數(shù)的?counter?變數(shù):
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <p>局部變量計(jì)數(shù)。</p> <p id="demo">0</p> <script> document.getElementById("demo").innerHTML = add(); function add() { var counter = 0; function plus() {counter += 1;} plus(); return counter; } </script> </body> </html>
如果我們能在外在中存取?plus()?函數(shù),這樣就能解決計(jì)數(shù)器的兩難。
我們同樣需要確保?counter = 0?只執(zhí)行一次。
我們需要閉包。