日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

匿名函数 闭包 ( 7 章 )

發布時間:2025/5/22 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 匿名函数 闭包 ( 7 章 ) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1: function functionName(arg0,arg1){ 2: ? 3: 4: ? 5: } 6: ? 7: var functionName = function( arg0 , arg1 ){ 8: ? 9: 10: ? 11: }

匿名函數時一種強大的令人難以置信的工具

這兩種的區別,? 前者會在代碼執行以前被加載到作用域中, 而后者則是在代碼執行到那一行的時候才會有定義.

另外的區別是, 函數聲明( 前者) 會給函數一個名字, 而函數表達式(后者) 則是創建一個匿名函數, 然后把這個匿名函數賦給一個變量 functionName.

關于函數聲明, 它的一個重要特征就是 函數聲明提升, 意思是在執行代碼之前會先讀取函數聲明, 這就意味著可以把函數聲明放在調用它的語句后面.

//匿名函數, 直接寫也是可以的,只不過沒有什么實際意義。

把函數想成值時, 都可以使用匿名函數 例如 :

在將函數作為參數傳入另一個函數,或者從一個函數中返回另一個函數時,通常都要使用以這種形式來定義匿名函數。

1: function createCompare(propertyName){ 2: ? 3: return function( object1, object2 ){ 4: ? 5: var value1 = object1[ propertyName] ; 6: ? 7: var value1 = object2[ propertyName] ; 8: ? 9: if ( value1 < value2 ){ 10: ? 11: return 1 ; 12: ? 13: }else { 14: ? 15: return -1; 16: ? 17: } 18: ? 19: 20: ? 21: } ; 22: ? 23: }
遞歸 ( 用途 1 )
1: <SPAN style="COLOR: #000000">function factorial( num ){ 2: ? 3: if(num <=1 ){ 4: ? 5: return 1 ; 6: ? 7: } else { 8: ? 9: return num * factorial( num -1 ) ; 10: ? 11: } 12: ? 13: } 14: ? 15: var anotherFactorial = factorial ; 16: ? 17: factorial = null ; 18: ? 19: alert( anotherFactorial(4) ) ; // 出錯, 因為此時 factorial = null , 已經不再可用, 所以第一次調用沒問題, 因為使用 anotherFactorial 指針, 可以找到對應的函數, 但是函數內部, 20: ? 21: // 由于是遞歸函數, 所以函數內部的名字 factorial 失效 ( null ) , 所以就會出錯 22: ? 23: return arguments.callee( num - 1 ) ; // 可以解決問題, 之前有提到過, 因此遞歸函數時, 使用 arguments.callee 總比使用函數名好 24: </SPAN>

使用 arguments.callee( num –1 );

1: function factorial( num ){ 2: ? 3: if (num <= 1){ 4: ? 5: return 1; 6: ? 7: } else ( 8: ? 9: return num * arguments.callee( num -1 ) // 其中 arguments.callee 是一個指向正在執行的函數的指針 10: ? 11: ) 12: ? 13: } 14: ? 15: var anotherFactorial = factorial ; 16: ? 17: factorial = null ; 18: ? 19: alert( anotherFactorial(4) ) ;
閉包

閉包是指 有權訪問另一個函數作用域中的變量的函數. 創建閉包的常見方式, 是在一個函數內部創建另一個函數,建議非必要時, 不要使用閉包,匿名函數也是差不多。

即,閉包是一個函數,是能夠訪問別的函數作用域變量的函數,那么內部函數肯定能訪問外部函數的變量, 所以,此時就用到了匿名函數 ( 閉包 ), 所以閉包和匿名函數是什么關系的,是包含關系,即閉包肯定是個匿名函數。例如:

1: function createComparisonFunction(propertyName){ 2: ? 3: return function( object1, object2){ 4: ? 5: var value1 = object1[propertyName]; 6: ? 7: var value2 = object2[propertyName]; 8: ? 9: if (value1 < value2){ 10: ? 11: return -1; 12: ? 13: } else if ( value1 > value2){ 14: ? 15: return 1; 16: ? 17: } else { 18: ? 19: return 0; 20: ? 21: } 22: ? 23: }; 24: ? 25: }

內部函數訪問了外部函數變量 prototyName,即使這個內部函數被返回了,而且是在其他地方被調用了,它仍然可以訪問變量 prototyName,內部函數的作用域鏈包括外部函數,

當某個函數第一次被調用時, 會創建一個執行環境及相應的作用域鏈, 并把作用域鏈賦值給一個特殊的內部屬性[Scope] , 然后, this, arguments 和其他命名參數的值來初始化函數的活動對象.但在作用域鏈中, 內部函數-> 外部函數-> 更外部函數->......-> 最外部函數. ( 這種包含關系 )

1: function compare(value1, value2){ 2: if(value1 < value2){ 3: return -1; 4: }else if(value1 > value2){ 5: return 1; 6: }else{ 7: return 0; 8: } 9: 10: } 11: ? 12: var result = compare(5,10);

對于這個例子中compare()函數的執行環境,其作用域鏈中包含兩個變量對象,本地活動對象和全局變量對象,顯然,作用域鏈本質上是一個指向變量對象的指針列表,它只引用但不實際包含變量對象。

后臺的每個執行環境都有一個表示變量的對象-變量對象, 全局環境的變量對象始終存在, 而像compare()函數這樣的局部環境變量對象, 則只在函數執行的過程中存在, 在創建compare()函數時, 會創建一個預先包含全局變量對象的作用域鏈, 這個作用域鏈保存在內部的[[Scope]]屬性中, 當調用 compare()函數時, 會為函數創建一個執行環境, 然后通過復制函數的[[Scope]]屬性中的對象構建起執行環境的作用于鏈. 此后, 又有一個活動對象(在此作為變量對象使用) 被創建并被推入執行環境作用域鏈的前端, 對于這個例子 compare() 函數的執行環境而言, 其作用域鏈中包含兩個變量對象, 本地活動對象和全局變量對象, 顯然作用域鏈本質上是一個指向變量對象的指針列表, 它只引用但不實際包含變量對象.

無論什么時候在函數中訪問一個變量時,就會從作用域鏈中搜索具有相應名字的變量,一般來講,當函數執行完畢后,局部活動對象就會被銷毀,內存僅保存全局作用域,但是閉包的情況不同.

在另一個函數內部定義的函數會將包含函數( 即 外部函數 )的活動對象添加到它的作用域鏈中。

?

承接以上代碼例子 :

var compare = createComparisonFunction(“name”);? //compare指針保存函數地址

var result = compare( {name : “Nicholas”}, {name : “Greg”});

在匿名函數從 createComparisonFunction()中被返回后,它的作用域鏈被初始化為包含createComparisonFunction()函數的活動對象和全局對象,這樣,匿名函數就可以訪問createComparisonFunction()中定義的所有變量, 更為重要的是, createComparisonFunction()函數執行完畢后,其活動對象也不會被銷毀,因為匿名函數的作用域鏈仍然在引用這個活動對象。(那么匿名函數為什么沒有被釋放呢? 因為createComparisonFunction 這個函數本身返回的是一個函數, 所以compare=createComparisonFunction 這條語句的本質就是又做了一個指針指向了內存中的匿名函數, 所以在釋放空間時, 因為匿名函數在外圍函數的外圍還有一個指針指向, 所以它不能被釋放, 同樣它指向的外圍函數內部的變量也不能被釋放)換句話說,當createComparisonFunction()函數返回后,其執行環境的作用域鏈會被銷毀,但它的活動對象仍然會留在內存中,直到匿名函數被銷毀后,createComparisonFunction()的活動對象才會被銷毀。所以需要顯示解除 :? 例如:

// 創建函數

var compareNames = createComparisonFunction("name"); // 這是 compareNames 已經是指向了函數內部的匿名函數

// 調用函數, // 注意, 這個函數執行完畢后, 函數的內存空間將被釋放, 但是... 匿名函數和匿名函數所指向的函數的內部變量不能被釋放

var result = comareNames({name: "Nicholas"}, {name: “Greg”});

// 解除對匿名函數的引用( 以便釋放內存 )

compareNames = null;

注意: ( anonymous ) Scope 中的 this 是 window, 這也就呼應了下邊的 this 的問題.

關于函數參數傳遞(特別是匿名函數 )

argument 1 /* 匿名函數傳參辦法 */ 2 3 /* 01. 普通函數 */ 4 5 var aa = createFunction( xx ); // xx為實參 6 7 /* 02. 匿名函數 */ 8 var aa = function(num){}(argu); // num為形參, argu為實參

?

作用域鏈的副作用,即閉包只能取得包含函數中的任何變量的最后一個值,而不是某個特殊變量。

1: function createFunctions(){ 2: var result = new Array(); 3: for(var i=0; i<10; i++){ 4: result[i] = function(){ 5: return i; 6: }; 7: } 8: return result; 9: } 10: ? 11: var funcs = createFunctions(); 12: //每個函數都輸出10 13: for(var i=0; i<funcs.length; i++){ 14: document.write(funcs[i]()); 15: }

注意:

var funcs = createFunctions();? // 這個 createFunctions() 函數會執行, 并將返回結果 賦值給 funcs, 那么返回的是數組, funcs 就是數組, 返回的是函數 funcs 是函數.

var funcs = createFunctions;??? // 這個 createFunctions 函數不會執行, 只是又定義了一個指針指向該函數.

首先, 來說createFunction函數的返回結果, 它所返回的不是一個數字, 而是一個函數, 所以, 當我們接到返回結果時, 因為是一個函數, 而funcs[i]() 表示執行這個函數, 而此時執行這個函數, 這個函數只有1條語句 return i, 而這時因為已經退出了 createFunction函數, 所以i的值是9. 所以最后顯示的結果全是9, 之前有誤區 :

誤區1 createFunctions()函數返回的數值, 首先 return result 是返回語句, 表示此函數返回的是一個數組, 那么數組中裝的是什么的, 很直接的是函數, 而并非是 i 的值, 如果是i的值, 那么后面輸出時也就不用寫成funcs[i]()了, 而直接寫成 funcs[i]就可以了. 注意閉包內部名沒有執行, 因為開始是個賦值語句, result[i]=function(){}; 這個賦值成功了就可以了, 函數內部并沒有執行, 函數必須后面跟()后才能執行, 剩下的就是單純的賦值, 不要想的那么復雜.

誤區2 以為result[i]=function(){}; 這一步會執行function里的內容, 其實直到funcs[i]() 這個語句的時候, 閉包里的內容才真正的執行, 包括 var funcs = createFunctions(); 這條語句, 閉包都沒有執行, 這條語句時 createFunction 這個函數執行了, 并把結果 包含有函數的數組返回給了funcs, 這個時候 funcs就是一個數組了, 并且數組元素是一個function, 所以才可以調用 funcs[i](), 來執行數組元素函數.

上例中,每個函數都返回10,而不是1,2,3,4,5,6,7,8,9,10,因為每個函數的作用域鏈中都保存著createFunctions()函數的活動對象,所以它們引用的都是 同一個變量 i , 當createFunctions()函數返回后,變量 i 的值是 10 , 此時每個函數都引用這保存變量 i 的同一個變量對象,(如上, var funcs = createFunctions() 這條語句會執行 createFunctions函數, 執行后, 函數內的 i 的值就是10了, 并且將數組返回給了 funcs, 此時 funcs 就是一個數組, 而數組元素是 函數, 注意, 此處的數組元素并非數字, 所以當下邊又執行 document.write(funcs[i]()))時, 實際上是在執行數組元素里的函數, 也就是匿名函數, 而此時, 匿名函數會去調用createFunctions 中的 i , 而此時, i 已經是10了, createFunction這個函數還在作用域鏈的原因就是因為有匿名函數還在引用它. 所以整個 createFunction函數的作用域都沒有被釋放. )

?

所以在每個函數內部 i 的值都是 10。改進如下 :

1: function createFunctions(){ 2: var result = new Array(); 3: for(var i=0; i<10; i++){ 4: result[i] = function(num){ 5: return function(){ 6: return num; 7: }(i); 8: 9: }; 10: } 11: return result; 12: } 13: ? 14: var funcs = createFunctions(); 15: ? 16: for(var i=0; i<funcs.length; i++){ 17: document.write(funcs[i]()); 18: }

這里, 我們沒有直接把閉包賦值給數組, 而是定義了一個匿名函數, 并立即執行該匿名函數, 將結果賦值給數組, 這里匿名函數的參數是 num, 每次執行時會將 i 傳遞進去, 由于函數參數是按值傳遞的, 所以就會將變量 i 的當前值賦值給參數 num, 而在這個匿名函數內部, 又創建了一個訪問 num 的閉包, 這樣一來, result 數組中每個函數都有自己的 num 變量的一個副本, 因此就有各自不同的值了. 注意這里的匿名函數, 如下:

result[i] = function(num) {? // 匿名函數

? return function() {??? // 閉包, 所有的閉包都是匿名函數

??? return num;

? }

}(i);??? // (i) 表示要執行

這個匿名函數之所以能夠立刻執行, 就是因為后邊的(i), 函數名稱后邊帶(), 就表示要執行函數, 正式因為要執行函數, 所以 var funcs = createFunctions(); 要執行函數 createFunctions, 而進入createFunction 后, 又要執行函數 function(num), 注意上邊例子的閉包是不需要執行這個的, 這里就有一個閉包, result[i] 里邊存放的也都是函數, result[i] 里的函數不需要執行, 但是此時的 num 所處的環境是在 function(num) 這里, 這里每個 num 的值已經不同, 當再執行 document.write(funcs[i]()); 調用閉包的匿名函數時, 同樣此時在執行 result[i] 里的函數, 而這時的函數所返回的內容是 num, 而 num 因為每個所處的環境不同, 都在各自的 function(num) 環境中, 所以它們之間沒有影響, 所以最后返回的結果是, 1,2,3,4,5,6,7,8,9,10.

?

function createFunction() {var result = new Array();for (var i=0; i<=9; i++) {result[i] = function() {return i; }();}return result; }var funs = createFunction(); for(var i=0; i<funs.length; i++) {alert(funs[i]); }

所以, 以上是更簡單的得到0,1,2,3,4,5,6,7,8,9的方法, 只要在原來的基礎上, result[i] = function(){return i;}(); 加上這一對小括號, 讓這個閉包在函數內部執行就可以了

?

?

this對象

在閉包中使用 this 對象也可能會有點問題,this對象是在運行時基于函數的執行環境綁定的,在全局函數中,this等于window,在函數被作為某個對象的方法調用時,this等于那個對象。不過, 匿名函數的執行環境具有全局性, 因此 this 對象通常指向 windows.

1: var name = "The window"; 2: ? 3: var object = { 4: name : "My Object", 5: getNameFunc : function(){ 6: return function(){ 7: return this.name; 8: } 9: } 10: }; 11: ? 12: alert(object.getNameFunc()); //"The window"

以上代碼先創建了全局變量 name, 又創建了一個包含 name 屬性的對象, 這個對象還包含一個方法-getNameFunc(), 它返回一個匿名函數, 而匿名函數又返回 this.name,

為什么上邊例子執行結果是 The window ?

前面曾經提過, 每個函數在被調用時, 其活動對象都會自動取得兩個特殊變量 this 和 arguments, 內部函數在搜索這兩個變量時, 只會搜索到活動對象為止, 因此永遠不可能訪問到外部函數中的這兩個變量, 不過, 把外部作用域中的 this對象保存在一個閉包能夠訪問的到的變量里, 就可以讓閉包訪問到該變量了,

個人總結, 因為閉包會將它所在的函數的作用域鏈全部保存起來, 所有就有類似的事情, 閉包->外部函數->再外部函數->全局, 這是閉包的作用域鏈, 因為閉包全部保存了, 而又由于閉包只能取得以上作用域鏈的最后值, 例如上邊例子 i, 都是顯示10 一樣, 所以當你定義的變量名字 name 與環境變量同名時, 閉包要搜索到作用域鏈最后的這個 name, 所以就后這個例子就返回了 全局 name 屬性.

?

匿名函數的執行環境具有全局性,所以它的 this 屬性都是 window, 并且 this 是關鍵字。看下邊例子 :

this 1 var name = "The window"; 2 3 var object = { 4 name : "My object", 5 getNameFunc : function(){ 6 var that = this; // 此時是可以訪問this的, 因為this是 7 // 這個對象的一個屬性, 8 return function() { // 閉包 9 return that.name; // that 作為外層函數的活動對象, 10 // 會被引用(注意不是this, this不會 11 // 被引用, 12 }; 13 } 14 };

之所以這個會顯示我們想要的結果, 是因為在這個作用域鏈上沒有 that 同名的屬性, 所以當閉包搜索 that 屬性時, 這個 Object 里的 that 就是最頂端了, 換句話說是最后了, 所以自然就要顯示這個 that 的結果了.

模仿塊級作用域

JavaScript中沒有塊級作用域的概念, 例如 for 循環中的 i , 當離開 for 循環時, 也還繼續存在, 而在 C 和 Java 這些語言中就不一樣了,

function outputNumbers(count) {

? for (var i=0; i<count; i++) {

??? alert(i);

? }

? alert(i);? // 此處的 i, 仍然可以調用, 因為沒有塊級別作用域

}

?

一般來說, 我們應該盡量少向全局作用域中添加變量和函數. 通過這種創建私有作用域, 每個開發人員既可以使用自己的變量, 又不必擔心搞亂全局作用域.

1: ( //此處的 ( 2: ? 3: function(){ 4: ? 5: //這里是塊級作用域 6: ? 7: } 8: ? 9: ) ( ) ; // 此處的 ) ( ) ;

以上代碼定義并立即調用了一個匿名函數, 將函數聲明包含在一對圓括號中, 表示它實際上是一個函數表達式, 而緊隨其后的另一對圓括號會立即調用這個函數.

如果只是單純的寫 function(){

??????????????????????? }();

這樣會出現錯誤,因為 Javascript 將 function 關鍵字當做一個函數聲明的開始,而函數聲明后面不能跟圓括號,然而,函數表達式后面可以跟圓括號(表示執行函數),要將函數聲明轉換成函數表達式,只要在function前后加一個圓括號就可以了, 即

( function {

}) ();

無論什么地方,只要臨時需要一些變量,就可以使用似有作用域,例如 :

似有作用域 1 function outputNumbers(count) { 2 (function() { 3 for (var i=0; i<count; i++) { 4 alert(i); 5 } 6 })(); 7 alert( i ); //導致一個錯誤 8 }

?

1: var someFunction = function () { 2: ? 3: // 這里是塊級別作用域 4: ? 5: } ; 6: ? 7: someFunction(); //調用函數

調用函數的方式是在函數名后邊加一對圓括號 (), 即 someFunction()

臨時使用一些變量時, 可以使用這種塊級作用域

1: ( 2: ? 3: function(){ 4: ? 5: for ( var i = 0 ; i < count ; i++ ) { 6: ? 7: alert( i ) ; 8: ? 9: } 10: ? 11: } 12: ? 13: ) () ; 14: ? 15: alert ( i ) ; // 導致一個錯誤
私有變量

JavaScript中沒有私有成員概念, 不過, 有一個私有變量的概念, 任何函數中定義的變量, 都可以認為是私有變量, 外部不能訪問函數內部的私有變量, 但是, 可以通過在函數內部創建一個閉包( 即內部函數) , 那么閉包通過自己的作用域鏈也可以訪問這些變量, 而利用這一點, 就可以創建用于訪問私有變量的公有方法.

我們把有權訪問私有變量和私有方法的公有方法稱為 特權方法.

第一種,構造函數中定義的特權方法:

1: function MyObject(){ 2: ? 3: var privateVariable = 10 ; //私有變量 4: ? 5: function privateFunction() { 6: ? 7: return false ; 8: ? 9: } 10: ? 11: this.publicMethod = function() { // 特權方法 12: ? 13: privateVariable++; 14: ? 15: return PrivateFunction(); 16: ? 17: } ; 18: ? 19: }

另一種方法:

1: ( function(){ 2: //似有變量和私有函數 3: var privateVariable = 10; 4: function privateFunction(){ 5: return false; 6: } 7: ? 8: //構造函數 9: MyObject = function(){}; 10: ? 11: //公有特權方法 12: MyObject.prototype.publicMethod = function(){ 13: privateVariable++; 14: return privateFunction(); 15: }; 16: } 17: 18: )();

Good 1 ( function() { 2 var name = ""; //私有變量 3 Person = function(value) { 4 name = value; 5 }; 6 Person.prototype.getName = function() { 7 return name; 8 }; 9 Person.prototype.setName = function(value) { 10 name = value; 11 }; 12 13 14 } ) (); 15 16 var person = new Person("Nicholas"); 17 alert( person.getName() ); // "Nicholas" 18 person.setName("Greg"); 19 alert( person.getName() ); // "Greg"

轉載于:https://www.cnblogs.com/moveofgod/archive/2012/09/21/2697440.html

總結

以上是生活随笔為你收集整理的匿名函数 闭包 ( 7 章 )的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。