js函數的幾種寫法 閉包概要


最常見的:

Js代碼
1 function foo() {  
2  alert('hi, js');  
3 }  
4 foo();  

用匿名函數:

Js代碼 
1 var foo = function () {  
2  alert('hi, js');  
3 }  
4 foo(); 

改裝一下,給調用的foo()加個括號:

Js代碼
1 var foo = function () {  
2  alert('hi, js');  
3 }  
4 (foo)();  

干脆,連foo的定義也省掉------把foo賦值語句的等號右邊的東東、直接替換掉剛才括起來的foo:

Js代碼
1 (function () {  
2  alert('hi, js');  
3 })();  

先看常用的函數:

 
1 function sayHello(name) {  
2   var text = 'Hello ' + name;  
3   var sayAlert = function() { alert(text); }  
4   sayAlert();  
5 }  
6 sayHello('Bob');  

 

閉包的例子

 

一句話描述:

  • 閉包是函數的局部變量,在函數return之后,仍然有值, 或者
  • 閉包是stack-frame,在函數return的時候,它不會被釋放。(就好像'stack-frame'是內存分配、而非處於堆棧!)

下面的代碼返回一個function的引用:

 

1 function sayHello2(name) {  
2   var text = 'Hello ' + name; // local variable  
3   var sayAlert = function() { alert(text); }  
4   return sayAlert;  
5 }  
6 var say2 = sayHello2('Jane');  
7 say2(); //hello Jane  

 

多數JavaScript程序員能夠理解上面代碼的函數引用如何返回給變量。請在學習閉包之前理解它。C程序員把函數看做返回的一個函數指針,變量sayAlert和say2分別是函數指針。


C函數指針和JavaScript的函數引用有着本質的不同。在JavaScript,函數引用變量既是一個函數指針,又是一個隱藏的閉包指針。

 

上面代碼擁有閉包,因為函數體內又聲明了一個匿名函數 function() { alert(text); } ,參看例子中的sayHello2()。如果你在另一個函數內使用function關鍵字,閉包就產生了。

 

在C和其他多數語言,當函數return之后,stack-frame就被銷毀了,所有的局部變量也就不能訪問了。

 

在JavaScript中,如果你在函數里聲明了函數,在你調用的函數renturn之后,局部變量仍然可以訪問。請注意上面的例子,我們調用了變量text,它是函數sayHello2的局部變量。

 

Example 3

 

這個例子表明局部變量不是拷貝傳遞,而是引用傳遞。在外層函數退出時,它把stack-frame保存在內存。

 1 function say667() {  
 2   // Local variable that ends up within closure  
 3   var num = 666;  
 4   var sayAlert = function() { alert(num); }  
 5   num++;  
 6   return sayAlert;  
 7 }  
 8 var sayNumba = say667();  
 9 sayNumba(); //667,而不是666  
10 alert(sayNumba.toString());  

 

Example 4

 

三個函數對某個閉包使用同一個引用,因為它們均在setupSomeGlobals()里聲明的。

 1 var gAlertNumber = gIncreaseNumber = gSetNumber = null;  
 2 function setupSomeGlobals() {  
 3   // Local variable that ends up within closure  
 4   var num = 666;  
 5   // Store some references to functions as global variables  
 6   gAlertNumber = function() { alert(num); }  
 7   gIncreaseNumber = function() { num++; }  
 8   gSetNumber = function(x) { num = x; }  
 9 }  
10 setupSomeGlobals();  
11 //任意、多次 運行下面的函數  
12 gAlertNumber();  
13 gIncreaseNumber();  
14 gSetNumber(5); //把num重新設為 5  
15 gSetNumber(-8888); //把num重新設為 -8888  

 

重新運行setupSomeGlobals(); 就會重新產生一個新的閉包。在JavaScript中,當你在函數里又聲明一個函數,外部函數每調用一次,內部函數將再被重新產生一次。

 

Example 5

 

當心下面例子的循環:閉包中的局部變量可能和你最初想的不一樣。

 1 function buildList(list) {  
 2   var result = [];  
 3   for (var i = 0; i < list.length; i++) {  
 4     var item = 'item' + list[i];  
 5     result.push( function() {alert(item + ' ' + list[i])} );  
 6   }  
 7   return result;  
 8 }  
 9   
10 function testList() {  
11   var fnlist = buildList([1,2,3]);  
12   // using j only to help prevent confusion - could use i  
13   for (var j = 0; j < fnlist.length; j++) {  
14     fnlist[j]();  
15   }  
16 }  
17   
18 testList();  //輸出3次:'item3 undefined'  

 

Example 6

 

下面的例子表明,閉包包含了 在外部函數退出之前、定義的任何局部變量。注意,變量alice實際上在匿名函數之后聲明的。匿名函數先被聲明:當函數被調用時,它可以訪問alice,因為alice在閉包里。

 

1 function sayAlice() {  
2   var sayAlert = function() { alert(alice); }  
3   // Local variable that ends up within closure  
4   var alice = 'Hello Alice';  
5   return sayAlert;  
6 }  
7 sayAlice()(); //Hello Alice  
8 alert(alice); //錯誤:alice不是全局變量,它在函數體內var了  

 

Example 7


下面的例子表明,每次調用會產生各自的閉包。

 

 1 function newClosure(someNum, someRef) {  
 2   // Local variables that end up within closure  
 3   var num = someNum;  
 4   var anArray = [1,2,3];  
 5   var ref = someRef;  
 6   return function(x) {  
 7       num += x;  
 8       anArray.push(num);  
 9       alert('num: ' + num +   
10           '\nanArray ' + anArray.toString() +   
11           '\nref.someVar ' + ref.someVar);  
12     }  
13 }  
14 closure1 = newClosure(40, {someVar : 'closure 1'});   
15 closure1(5);  
16   
17 closure2 = newClosure(1000, {someVar : 'closure 2'});  
18 closure2(-10);  

小結


讀一些說明要比理解上面的例子難得多。我對於閉包的說明以及stack-frame等等在技術上可能不正確 --- 但是它們的確有助於理解


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM