亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

學習Javascript閉包(Closure)

系統 2628 0

包(closure)是Javascript語言的一個難點,也是它的特色,很多高級應用都要依靠閉包實現。

下面就是我的學習筆記,對于Javascript初學者應該是很有用的。

一、變量的作用域

要理解閉包,首先必須理解Javascript特殊的變量作用域。

變量的作用域無非就是兩種:全局變量和局部變量。

Javascript語言的特殊之處,就在于函數內部可以直接讀取全局變量。

  var n=999;

  function f1(){
    alert(n);
  }

  f1(); // 999

另一方面,在函數外部自然無法讀取函數內的局部變量。

  function f1(){
    var n=999;
  }

  alert(n); // error

這里有一個地方需要注意,函數內部聲明變量的時候,一定要使用var命令。如果不用的話,你實際上聲明了一個全局變量!

  function f1(){
    n=999;
  }

  f1();

  alert(n); // 999

二、如何從外部讀取局部變量?

出于種種原因,我們有時候需要得到函數內的局部變量。但是,前面已經說過了,正常情況下,這是辦不到的,只有通過變通方法才能實現。

那就是在函數的內部,再定義一個函數。

  function f1(){

    var n=999;

    function f2(){
      alert(n); // 999
    }

  }

在上面的代碼中,函數f2就被包括在函數f1內部,這時f1內部的所有局部變量,對f2都是可見的。但是反過來就不行,f2內部的局部變量,對f1就是不可見的。這就是Javascript語言特有的"鏈式作用域"結構(chain scope),子對象會一級一級地向上尋找所有父對象的變量。所以,父對象的所有變量,對子對象都是可見的,反之則不成立。

既然f2可以讀取f1中的局部變量,那么只要把f2作為返回值,我們不就可以在f1外部讀取它的內部變量了嗎!

  function f1(){

    var n=999;

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999

三、閉包的概念

上一節代碼中的f2函數,就是閉包。

各種專業文獻上的"閉包"(closure)定義非常抽象,很難看懂。我的理解是,閉包就是能夠讀取其他函數內部變量的函數。

由于在Javascript語言中,只有函數內部的子函數才能讀取局部變量,因此可以把閉包簡單理解成"定義在一個函數內部的函數"。

所以,在本質上,閉包就是將函數內部和函數外部連接起來的一座橋梁。

四、閉包的用途

閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數內部的變量,另一個就是讓這些變量的值始終保持在內存中。

怎么來理解這句話呢?請看下面的代碼。

  function f1(){

    var n=999;

    nAdd=function(){n+=1}

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999

  nAdd();

  result(); // 1000

在這段代碼中,result實際上就是閉包f2函數。它一共運行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數f1中的局部變量n一直保存在內存中,并沒有在f1調用后被自動清除。

為什么會這樣呢?原因就在于f1是f2的父函數,而f2被賦給了一個全局變量,這導致f2始終在內存中,而f2的存在依賴于f1,因此f1也始終在內存中,不會在調用結束后,被垃圾回收機制(garbage collection)回收。

這段代碼中另一個值得注意的地方,就是"nAdd=function(){n+=1}"這一行,首先在nAdd前面沒有使用var關鍵字,因此nAdd是一個全局變量,而不是局部變量。其次,nAdd的值是一個匿名函數(anonymous function),而這個匿名函數本身也是一個閉包,所以nAdd相當于是一個setter,可以在函數外部對函數內部的局部變量進行操作。

五、使用閉包的注意點

1)由于閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。

2)閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。

六、思考題

如果你能理解下面兩段代碼的運行結果,應該就算理解閉包的運行機制了。

代碼片段一。

  var name = "The Window";

  var object = {
    name : "My Object",

    getNameFunc : function(){
      return function(){
        return this.name;
      };

    }

  };

  alert(object.getNameFunc()());


代碼片段二。

  var name = "The Window";

  var object = {
    name : "My Object",

    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };

    }

  };

  alert(object.getNameFunc()());

(完)

<script src="/newwindow.js" type="text/javascript"></script>

相關文章

功能鏈接

留言(53條)

講的很清楚明了,連我都懂了,要是我們大學時的老師也能這么講課。。。他們只會放幻燈片

這里有個 PPT 用于說明 JS 閉包,說明得很透徹: http://www.gracecode.com/archives/2385/

呵呵,可以作為面試題了!

閉包個人感覺是一種描述函數內部的數據結構,來描述函數的運行上下文.Javascript編程精粹 這本書算是講的比較好一點.

類是有行為的數據,閉包是有數據的行為。

阮兄:
有點疑問:
function f1(){

    n=999;

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999
可以寫成如下的不也一樣么?

function f1(){

    n=999;

    return n;

  }
var result=f1();
alert(result);

@tt 實際上后種方法每次調用 f1 時,都會聲明 n = 999,而且 n 無法保留狀態值(嚴格按照你的代碼,其實 n 為全局變量,我理解你的本意為 var n = 999;)。

而第一種 f1 實際上返回的是個匿名函數,這樣 n 作用域被另外個 f2 函數作用域所使用,因此它會保留。n 不會被重復聲明,且內容會被保存

這是我見過最簡單易懂的閉包教程。

支持下。

博主的博客寫的不錯,簡單易懂,東西涉及的很多方面我都有興趣,看來是同道中人,^_^

一文中的!!!!!!!!!!!!
學習了!!

想知道思考題的答案,
我以為是:My Object

頂樓主,我讀了一些文章。不是特明白。
有個問題。
記得有人說。外面的函數是closure,
好像樓主說里面的函數是closure.

不知道到底哪個是?謝謝。

樓主講講最后一個思考題,沒明白

請版主講一講最后一個例子怎么回事,沒有看明白

函數中的this一般是指向window中的變量。

                引用hou的發言:
              
請版主講一講最后一個例子怎么回事,沒有看明白

上面本人說得不太正確。
this的指向是由它所在函數調用的上下文決定的,而不是由它所在函數定義的上下文決定的。

如果非要指向object,可顯式的控制--把代碼的最后一句改為 alert(object.getName().call(object));

阮大哥講的很透徹 受益匪淺

大道至簡,給予我這個初學者很大的幫助,謝謝!

淺顯易懂,很好。

如下看法,認為有待商榷:
#1、有一個地方需要注意,函數內部聲明變量的時候,一定要使用var命令。如果不用的話,你實際上聲明了一個全局變量!

#2、這段代碼中另一個值得注意的地方,就是“nAdd=function(){n+=1}”這一行,首先在nAdd前面沒有使用var關鍵字,因此nAdd是一個全局變量,而不是局部變量。其次,nAdd的值是一個匿名函數(anonymous function),而這個匿名函數本身也是一個閉包,所以nAdd相當于是一個setter,可以在函數外部對函數內部的局部變量進行操作。

  function f1(){
test = 10;
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
//如果 #1 說法正確,下句會打印10,實際結果是test未定義。
//alert(test); // error test 未定義

//如果 #2 正確,語句 nAdd(); 位置在何處應該都能執行,測試結果在下面這個位置,也就是語句 var result=f1(); 前。是不能執行的。
//nAdd();
var result=f1();
result(); // 999
  nAdd();
result(); // 1000

To 過客:

函數內部定義的方法和變量,要等到函數執行過以后,才會真正定義

但是從過客說的里面可以引出另外的問題,當使用這樣的代碼時。

  function f1(){
test = 10;
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }

如果在函數f1定義之前添加變量定義

              var n = 1;
            
然后調用
              f1()();
            
則顯示為999。說明nAdd中的n確實是作為全局變量存在。于是問題就來了——有什么方法讓他可以是父函數中定義的n呢?

?

大道至簡,很不錯!~ 這篇文章我要轉了...

                引用George Wing的發言:
              

函數中的this一般是指向window中的變量。

this關鍵字代表的實例會根據環境不同而變化的. 他總是指向owner 看看這篇你大概就動this這個關鍵字了

http://www.quirksmode.org/js/this.html

最后一個題感覺和閉包沒什么關系啊,能詳細解釋一下嗎?因為當一個函數作為函數而不是方法來調用的時候,this指向的是全局對象,這在《Javascript權威指南》上說的很清楚,所以答案肯定是“The Window”,和閉包沒什么關系啊

最后一題重點在this

如果把f2申明成全局變量,道理一樣嗎?

太經典了!
終于理解了,一箭雙雕啊!既理解了this的用法,又理解了閉包

這個例子很不錯,真的是一箭雙雕

前面講得挺好的,淺顯易懂。對最后的兩個例子搞不清楚為啥。版主能不能具體分析下。
var obj=function()
{
var MyFunc=function()
{
alert("hello world");
}
return function()
{
return MyFunc;
}
}()
var f3=obj();
var f4=obj();
alert(f3 === f4);//為啥是TRUE;搞不懂

最后兩個例子很精煉 ^ ^

嘗試解答代碼段一:
getNameFunc: function() {//假設函數名為A
return function()/*假設函數名為B*/ { return this.name; };
}
在函數里面構建函數的時候,閉包產生。
在函數B內調用函數A的this.name,由于函數A沒有name屬性,所以就去找全局變量name,找到了,所以返回“The Window”,要是沒有找到,則返回“undefined”。

代碼段二可以嘗試將代碼更改為:
var _this = this;
return function() { return _this.name +"__"+ this.name; };

只有一點沒弄懂,如下代碼,nAdd在函數外為什么可以有意義?而test不行?想了好久,不知道那里有解答
  function f1(){
test=10;
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }

通俗易懂,, 閱覽無數教程, 看了這篇, 終于明白了點.

寫得太好了

這篇文章是阮兄一貫的風格,我喜歡,不過 "Javascript語言的特殊之處,就在于函數內部可以直接讀取全局變量。"這句有點奇怪,c不一樣可以在函數內部直接讀取全局變量么?難道不是么?

阮大哥能不能具體講下最后的思考題啊? 感覺關鍵在this

變量的作用域無非就是兩種:全局變量和局部變量。
這句話值得商榷, 變量的作用域確實只有兩種, 不過他們是全局對象和函數.
你想說的或許是變量的類型有兩種?

理解最后兩個例子:

1 函數中的this指的是調用這個函數的owner
2 object.getNameFunc()是返回一個函數,并沒有執行函數中的代碼
3 增加一個例子0:

var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
return (this.name);
      }
  };
var name = object.getNameFunc();
  alert(name);

4 把例子1變成

  var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name; //這個this是有上下文的限制的
      };
    }
  };
var tmp = Object.getNameFunc(); //此時沒有執行this.name
var name = tmp();//這個時候才執行,這時候的this上下文為全局
alert(name);
//alert(object.getNameFunc()())

5 把例子2變成:

var name = "The Window";

  var object = {
    name : "My Object",

    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };
    }
  };
var tmp = Object.getNameFunc(); //這個時候執行了that = this,這里的this上下文是object,所以that指的是object
var name = Object.getNameFunc(); //這個時候執行了that.name
alert(name);
//alert(object.getNameFunc()());

                引用Jason的發言:
              

但是從過客說的里面可以引出另外的問題,當使用這樣的代碼時。

  function f1(){
test = 10;
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }

如果在函數f1定義之前添加變量定義var n = 1;然后調用f1()();則顯示為999。說明nAdd中的n確實是作為全局變量存在。于是問題就來了——有什么方法讓他可以是父函數中定義的n呢?

              var n = 1;
function f1(){
	var n = 999;
	nAdd = function(){
		n++;
	}
	function f2(){
		alert(n);
	}
	return f2;
}
var b = f1();
nAdd();//n = 999+1 = 1000
b();//彈出 n 的值是 1000 (閉包內的變量n)
alert(n);//彈出n的值是 1 (全局)

            
全局的函數 nAdd 和 由f1返回的函數中 所使用到的變量n全部為f1函數內的局部變量n,而不是全局變量n,證據就是上面的代碼中最后一句 alert(n) 彈出的值是1, 也就是說 nAdd中的n++并沒有改變全局變量中n的值.

你自己描述的是 f1()() 顯示的是999,說明 n 是使用的f1內部的變量n,而非是全局變量n,不知道你為什么會有

說明nAdd中的n確實是作為全局變量存在。
這種想法呢? 如果想在nAdd中使用全局變量n(即在函數外面定義的n)的話,使用window.n來訪問.

?

樓主文章中的:


二、如何從外部讀取局部變量?

這一整大段中的
n=999;

根據整篇文章所表達的內容,應該為:
var n=999;

因為如果沒有加var,則聲明的是全局變量,既然是全局變量,則在所有函數內部都是可見的,也就不會存在閉包這種說法.

?

請求樓主修正.

                引用小彘的發言:
              

前面講得挺好的,淺顯易懂。對最后的兩個例子搞不清楚為啥。版主能不能具體分析下。
var obj=function()
{
var MyFunc=function()
{
alert("hello world");
}
return function()
{
return MyFunc;
}
}()
var f3=obj();
var f4=obj();
alert(f3 === f4);//為啥是TRUE;搞不懂

因為f3和f4都指向同一個地址(即MyFunc).


var obj = (function() {
var MyFunc=function() {
alert("hello world");
}
return function() {
return MyFunc;
}
})();
var f3=obj();
var f4=obj();
alert(f3 === f4);//為啥是TRUE;搞不懂

?

我測試了一下,為什么第一個例子輸出的什么都沒有是null,第二個我理解是myobject。誰能解釋下

                引用小秦的發言:
              

請求樓主修正.

謝謝指出,已更正。

起初以為函數內用var聲明變量,就等于用了this聲明,其實不是
var w=100;
function f1(){
//var w=101;
//this.w=102;
function f2(){
document.write(this.w);
}
return f2;
}
f1()();
輸出:100

var w=100;
function f1(){
var w=101;
//this.w=102;
function f2(){
document.write(this.w);
}
return f2;
}
f1()();
輸出:100

var w=100;
function f1(){
w=101;
//this.w=102;
function f2(){
document.write(this.w);
}
return f2;
}
f1()();
輸出:101

var w=100;
function f1(){
//var w=101;
this.w=102;
function f2(){
document.write(this.w);
}
return f2;
}
f1()();
輸出:102

看起來函數中的var和this并不是一個概念,函數內的局部變量與函數的屬性不是一回事,不過通過上面的情況能夠加深理解this和閉包

很不錯的講解,樓主寫的通俗易懂,很棒的理解,很受用!我的qq:290913917 希望有機會成為共同研究javascript和html5的伙伴,謝謝!

我感覺第一個思考題是不是這樣理解:
首先this指向的是當前運行該函數的對象,
1、object.getNameFunc()得到了一個函數,函數為function(){return this.name}
2、object.getNameFunc()(),此時為window運行該函數,所以this指向的是window,所以this.name為The window

做習題之前有一點需要很清楚:

內部函數可以訪問定義它們的外部函數的參數和變量(除了this和arguments之外)
如果需要訪問對象的name屬性的話,就需要顯示的定義一個變量that來引用this,而這個變量此時就指向object對象了。

第一題改成下面這樣就很清楚了。getNameFunc的第一個()是屬于方法調用,所以this綁定到了object對象,自然this.name為"My Object",但是閉包函數無法訪問這個this,它只能訪問到全局的this。

var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
alert(this.name);
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());

寫的真不錯。。。看了很多文章講閉包都是云里霧里的。。看了本文才恍然大悟。。。哦原來閉包如此簡單。。。。樓主寫的不錯。。。

前面講的我都明白,但是最后兩個例子還是不明白,好多處都不懂!
1. var object = {。。。} 這是在干什么?是在聲明一個變量?還是在聲明一個類,然后里面有許多屬性?

2 . object.getNameFunc()(); 怎么會有兩個括號?


3. 如何判斷 this指向的是object 對象還是全局對象 ?

閉包是運行時中的概念,不能講哪個函數是一個閉包!而是哪個函數在運行時存在一個閉包!有時候,好幾個函數都可以組成一個閉包呢:
function ff()
{
var local=1;
this.add1=function()
{
return ++local;
};
this.add2=function()
{
return ++local;
}
}

var f=new ff();

alert(f.add1());//2
alert(f.add2());//3

最后兩個例子中,第一個其實不是閉包,第二個是,但第二個例子其實不用那么復雜,直接把第一個例子中的this去掉就可以了。

為什么第一個運行以后結果是result?!既不是window也不是object....???

學習Javascript閉包(Closure)


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 天天干天天射天天舔 | 在线综合 亚洲 欧美中文字幕 | 亚洲精品日韩精品一区 | www.欧美| 老司机午夜视频在线观看 | 亚洲欧美国产高清va在线播放 | 国产中文字幕在线免费观看 | 国产高清视频在线免费观看 | 小明看看成人免费 | 在线观看99| 精品无码久久久久久国产 | 国产福利一区二区精品视频 | 欧美激情(一区二区三区) | 亚洲精品午夜久久久伊人 | 国产综合婷婷 | 久草手机在线观看 | 中文视频在线观看 | 日韩在线第二页 | 在线99视频| 精品国产第一国产综合精品gif | 福利视频午夜 | 一级特黄特色aa大片 | 国产99视频精品免视看9 | 色狠狠一区二区 | 日韩视频一区二区三区 | 青青久久国产 | 国产成人精品亚洲一区 | 婷婷四房综合激情五月性色 | 2022国产男人亚洲欧美天堂 | 999毛片免费 | 日本精品久久久久中文字幕8 | 毛片基地免费视频a | 午夜欧美性视频在线播放 | 日本一级毛一级毛片短视频 | 性一级视频 | 亚洲精品午夜级久久久久 | 日韩a一级欧美一级在线播放 | 久久国产免费观看精品3 | 婷婷综合激情 | 成人精品一级毛片 | 96精品视频在线播放免费观看 |