DIV CSS 佈局教程網

JS閉包介紹與示例
編輯:JavaScript基礎知識     

閉包是指有權訪問另一個函數作用域的變量的函數。

閉包的局部變量可以在函數執行結束後仍然被函數外的代碼訪問。這意味著函數必須返回一個指向閉包的“引用”,或將這個”引用”賦值給某個外部變量,才能保證閉包中局部變量被外部代碼訪問。

在ECMAScript中,函數對象中定義的 內部函數(inner function) 是可以直接訪問外部函數的局部變量,創建閉包的常見方式,就是在一個函數內部創建另一個函數。

function welcome (name) {
var text = 'Hello ' + name; // local variable
// 每次調用時,產生閉包,並返回內部函數對象給調用者
return function () {
console.log(text);
}
}
var sayHello = welcome( "Viewer" );
sayHello() // 通過閉包訪問到了局部變量text
代碼的執行結果是:Hello Viewer,因為 sayHello() 函數在 welcome() 函數執行完畢後,仍然可以訪問到定義在其內部的局部變量 text ,這就是閉包的效果。

在ECMAscript的腳本的函數運行時,每個函數關聯都有一個執行上下文場景(Execution Context) ,這個執行上下文場景中包含三個部分:

文法環境(The LexicalEnvironment)
環境記錄(Enviroment Recode)
外部引用(指針)
變量環境(The VariableEnvironment)
局部變量
參數變量
this綁定
外部引用指向了外部函數對象的上下文執行場景。全局的上下文場景中此引用值為 NULL。這樣的數據結構就構成了一個單向的鏈表,每個引用都指向外層的上下文場景。

例如上面我們例子的閉包模型應該是這樣,sayHello 函數在最下層,上層是函數 welcome,最外層是全局場景。如下圖:

因此當sayHello被調用的時候,sayHello會通過上下文場景找到局部變量text的值,因此在屏幕的對話框中顯示出”Hello Viewer”。

變量環境(The VariableEnvironment)和文法環境的作用基本相似。



閉包實例

前面的內容大致說明了Javascript閉包是什麼,閉包在Javascript如何實現,下面通過針對一些例子來深入了解閉包。

例子1:閉包中局部變量是引用而非復制

function say667() {
var num = 666;
var sayAlert = function() {
console.log(num);
}
num++;
return sayAlert;
}

var sayAlert = say667();
sayAlert() //667


例子2:多個函數綁定同一個閉包,因為他們定義在同一個函數內。

function setupSomeGlobals () {
var num = 666;

// 存儲一些函數的引用作為全局變量
gAlertNumber = function() {
console.log(num);
}

gIncreaseNumber = function() {
num++;
}

gSetNumber = function(x) {
num = x;
}
}

setupSomeGlobals(); // 為三個全局變量賦值
gAlertNumber(); //666

gIncreaseNumber();
gAlertNumber(); // 667

gSetNumber(12);
gAlertNumber(); //12


例子3:當在一個循環中賦值函數時,這些函數將綁定同樣的閉包

<script type="text/javascript">
window.onload = function(){
var lists = document.getElementsByTagName("li");
for(var i=0,l=lists.length; i < l; i++){
lists[i].onclick = function(){
var t = i;
return function(){
console.log(t+1)
}
}()
}
}
</script>

<body>
<h1>當在一個循環中賦值函數時,這些函數將綁定同樣的閉包</h1>
<ul>
<li id="a1">aa</li>
<li id="a2">aa</li>
<li id="a3">aa</li>
</ul>
<body>




例子4:外部函數所有局部變量都在閉包內,即使這個變量聲明在內部函數定義之後。

function sayAlice() {
var sayAlert = function() {
console.log(alice);
}

var alice = 'Hello Alice';
return sayAlert;
}
var helloAlice = sayAlice();
helloAlice();


例子5:每次函數調用的時候創建一個新的閉包。

function newClosure(someNum, someRef) {
var num = someNum;
var anArray = [1,2,3];
var ref = someRef;
return function(x) {
num += x;
anArray.push(num);
console.log('num: ' + num +
'\nanArray ' + anArray.toString() +
'\nref.someVar ' + ref.someVar);
}
}
closure1 = newClosure(40,{someVar:'closure 1'});
closure2 = newClosure(1000,{someVar:'closure 2'});

closure1(5); // num:45 anArray[1,2,3,45] ref:'someVar closure1'
closure2(-10);// num:990 anArray[1,2,3,990] ref:'someVar closure2'




由於閉包會攜帶包含它的函數的作用域,因此會比其它函數占用更多的內存,過度使用閉包會導致占用內存,所以只有在必要的時候再使用閉包。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved