日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

學(xué)習(xí)Javascript閉包(Closure)

系統(tǒng) 2785 0

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

下面就是我的學(xué)習(xí)筆記,對于Javascript初學(xué)者應(yīng)該是很有用的。

一、變量的作用域

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

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

Javascript語言的特殊之處,就在于函數(shù)內(nèi)部可以直接讀取全局變量。

  var n=999;

  function f1(){
    alert(n);
  }

  f1(); // 999

另一方面,在函數(shù)外部自然無法讀取函數(shù)內(nèi)的局部變量。

  function f1(){
    var n=999;
  }

  alert(n); // error

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

  function f1(){
    n=999;
  }

  f1();

  alert(n); // 999

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

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

那就是在函數(shù)的內(nèi)部,再定義一個函數(shù)。

  function f1(){

    var n=999;

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

  }

在上面的代碼中,函數(shù)f2就被包括在函數(shù)f1內(nèi)部,這時f1內(nèi)部的所有局部變量,對f2都是可見的。但是反過來就不行,f2內(nèi)部的局部變量,對f1就是不可見的。這就是Javascript語言特有的"鏈?zhǔn)阶饔糜?結(jié)構(gòu)(chain scope),子對象會一級一級地向上尋找所有父對象的變量。所以,父對象的所有變量,對子對象都是可見的,反之則不成立。

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

  function f1(){

    var n=999;

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999

三、閉包的概念

上一節(jié)代碼中的f2函數(shù),就是閉包。

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

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

所以,在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁。

四、閉包的用途

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

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

  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函數(shù)。它一共運行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數(shù)f1中的局部變量n一直保存在內(nèi)存中,并沒有在f1調(diào)用后被自動清除。

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

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

五、使用閉包的注意點

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

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

六、思考題

如果你能理解下面兩段代碼的運行結(jié)果,應(yīng)該就算理解閉包的運行機制了。

代碼片段一。

  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>

相關(guān)文章

功能鏈接

留言(53條)

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

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

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

閉包個人感覺是一種描述函數(shù)內(nèi)部的數(shù)據(jù)結(jié)構(gòu),來描述函數(shù)的運行上下文.Javascript編程精粹 這本書算是講的比較好一點.

類是有行為的數(shù)據(jù),閉包是有數(shù)據(jù)的行為。

阮兄:
有點疑問:
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 實際上后種方法每次調(diào)用 f1 時,都會聲明 n = 999,而且 n 無法保留狀態(tài)值(嚴(yán)格按照你的代碼,其實 n 為全局變量,我理解你的本意為 var n = 999;)。

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

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

支持下。

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

一文中的!!!!!!!!!!!!
學(xué)習(xí)了!!

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

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

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

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

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

函數(shù)中的this一般是指向window中的變量。

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

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

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

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

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

淺顯易懂,很好。

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

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

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

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

To 過客:

函數(shù)內(nèi)部定義的方法和變量,要等到函數(shù)執(zhí)行過以后,才會真正定義

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

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

如果在函數(shù)f1定義之前添加變量定義

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

?

大道至簡,很不錯!~ 這篇文章我要轉(zhuǎn)了...

                引用George Wing的發(fā)言:
              

函數(shù)中的this一般是指向window中的變量。

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

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

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

最后一題重點在this

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

太經(jīng)典了!
終于理解了,一箭雙雕啊!既理解了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() {//假設(shè)函數(shù)名為A
return function()/*假設(shè)函數(shù)名為B*/ { return this.name; };
}
在函數(shù)里面構(gòu)建函數(shù)的時候,閉包產(chǎn)生。
在函數(shù)B內(nèi)調(diào)用函數(shù)A的this.name,由于函數(shù)A沒有name屬性,所以就去找全局變量name,找到了,所以返回“The Window”,要是沒有找到,則返回“undefined”。

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

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

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

寫得太好了

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

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

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

理解最后兩個例子:

1 函數(shù)中的this指的是調(diào)用這個函數(shù)的owner
2 object.getNameFunc()是返回一個函數(shù),并沒有執(zhí)行函數(shù)中的代碼
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(); //此時沒有執(zhí)行this.name
var name = tmp();//這個時候才執(zhí)行,這時候的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(); //這個時候執(zhí)行了that = this,這里的this上下文是object,所以that指的是object
var name = Object.getNameFunc(); //這個時候執(zhí)行了that.name
alert(name);
//alert(object.getNameFunc()());

                引用Jason的發(fā)言:
              

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

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

如果在函數(shù)f1定義之前添加變量定義var n = 1;然后調(diào)用f1()();則顯示為999。說明nAdd中的n確實是作為全局變量存在。于是問題就來了——有什么方法讓他可以是父函數(shù)中定義的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èi)的變量n)
alert(n);//彈出n的值是 1 (全局)

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

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

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

?

樓主文章中的:


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

這一整大段中的
n=999;

根據(jù)整篇文章所表達的內(nèi)容,應(yīng)該為:
var n=999;

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

?

請求樓主修正.

                引用小彘的發(fā)言:
              

前面講得挺好的,淺顯易懂。對最后的兩個例子搞不清楚為啥。版主能不能具體分析下。
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。誰能解釋下

                引用小秦的發(fā)言:
              

請求樓主修正.

謝謝指出,已更正。

起初以為函數(shù)內(nèi)用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

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

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

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

做習(xí)題之前有一點需要很清楚:

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

第一題改成下面這樣就很清楚了。getNameFunc的第一個()是屬于方法調(diào)用,所以this綁定到了object對象,自然this.name為"My Object",但是閉包函數(shù)無法訪問這個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 對象還是全局對象 ?

閉包是運行時中的概念,不能講哪個函數(shù)是一個閉包!而是哪個函數(shù)在運行時存在一個閉包!有時候,好幾個函數(shù)都可以組成一個閉包呢:
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

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

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

學(xué)習(xí)Javascript閉包(Closure)


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

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

【本文對您有幫助就好】

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

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 泉州市| 永泰县| 临泉县| 巴林右旗| 文山县| 精河县| 吉林省| 瑞金市| 永嘉县| 孝昌县| 吴江市| 平利县| 伊通| 增城市| 兴化市| 遂平县| 沙田区| 五指山市| 静乐县| 齐河县| 福海县| 滨海县| 吉安市| 扶风县| 板桥市| 启东市| 卢氏县| 马龙县| 曲松县| 韩城市| 镇雄县| 长岛县| 璧山县| 万宁市| 吴江市| 江川县| 丰镇市| 油尖旺区| 武冈市| 包头市| 巴青县|