成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

閉包詳解

lunaticf / 2986人閱讀

摘要:函數(shù)中的閉包閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)。理解閉包預(yù)與變量此時(shí)返回注意觀察下面的輸出內(nèi)容,理解函數(shù)的調(diào)用時(shí)刻和把的賦值給變量時(shí)刻這個(gè)函數(shù)會(huì)返回長(zhǎng)度為的函數(shù)數(shù)組。

Javascript函數(shù)中的閉包

閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)。創(chuàng)建閉包的常見方式就是,在一個(gè)函數(shù)的內(nèi)部創(chuàng)建另一個(gè)函數(shù)。
有關(guān)創(chuàng)建作用域鏈以及作用域鏈有什么作用的細(xì)節(jié)對(duì)于徹底理解閉包至關(guān)重要。在作用域鏈中,外部函數(shù)的活動(dòng)對(duì)象始終處于第二位,外部函數(shù)的外部函數(shù)的活動(dòng)對(duì)象處于第三位,……直至作為作用域重點(diǎn)的全局環(huán)境。

圖1. compare函數(shù)作用域鏈?zhǔn)疽鈭D

以上代碼先定義了compare函數(shù),然后又在全局作用域中調(diào)用了它。當(dāng)?shù)谝淮握{(diào)用時(shí),會(huì)創(chuàng)建包含this、arguments、value1、value2的活動(dòng)對(duì)象。全局執(zhí)行環(huán)境的變量對(duì)象(包含result、 this、 compare)在compare()執(zhí)行環(huán)境的作用域鏈中則處于第二位。

圖2.Compare函數(shù)執(zhí)行時(shí)的作用域鏈

無論什么時(shí)候在函數(shù)訪問一個(gè)變量時(shí),就會(huì)從作用域鏈中搜索具有相應(yīng)名字的變量。一般來講,當(dāng)函數(shù)執(zhí)行完畢之后,局部活動(dòng)對(duì)象(這里指compare函數(shù)創(chuàng)建的對(duì)象,包含this、arguments、value1、value2)就會(huì)被銷毀,內(nèi)存中只保留全局作用域(全局執(zhí)行環(huán)境的 變量)。但是,閉包的情況又有所不同:

在另一個(gè)函數(shù)的內(nèi)部定義的函數(shù)會(huì)將包含函數(shù)(即外部函數(shù))的活動(dòng)對(duì)象添加到它的作用域鏈上。
var compareName=createComparationFunction("name");
    var result=compare({name:"Nicho"},{name:"Greg"});
    compareName=null;//解除對(duì)匿名函數(shù)的引用(以便釋放內(nèi)存)

注:compareName沒有函數(shù)聲明Function,是匿名函數(shù)。

在匿名函數(shù)從createComparationFunction函數(shù)中被返回之后,它的作用域鏈被初始化為包含createComparationFunction函數(shù)的活動(dòng)對(duì)象和全局作用對(duì)象。匿名函數(shù)可以訪問createComparationFunction函數(shù)中定義的所有變量。

更為重要的是:函數(shù)在執(zhí)行完畢之后,其活動(dòng)對(duì)象也不會(huì)銷毀,因?yàn)槟涿瘮?shù)的作用域鏈仍然在引用這個(gè)活動(dòng)對(duì)象,直到匿名函數(shù)被銷毀之后,createComparationFunction函數(shù)的活動(dòng)對(duì)象才會(huì)被銷毀。由于閉包包含它的函數(shù)的作用域,因此會(huì)比其他函數(shù)占用更多的內(nèi)存。

This:匿名函數(shù)具有全局執(zhí)行環(huán)境。

理解閉包預(yù)與變量:

function createFunctions(){
        var result=new Array();
        for(var i=0;i<10;i++){
            result[i]=function(){
                return i;
            };
        }
        //alert(result);此時(shí)返回[function (){return i;},...,function (){return i;}]
        return result; 
    }
    var arr=createFunctions();

//注意觀察下面的輸出內(nèi)容,理解函數(shù)的調(diào)用時(shí)刻和把i的賦值給變量時(shí)刻

alert(arr);//[function (){return i;},function (){return i;},...,function (){return i;}]
  alert(arr());//[10,10,……,10]

這個(gè)函數(shù)會(huì)返回長(zhǎng)度為10的函數(shù)數(shù)組。

表面上看,似乎每個(gè)匿名函數(shù)都應(yīng)該返回自己在數(shù)組result中的索引值,但實(shí)際上每個(gè)函數(shù)都返回10。因?yàn)槊總€(gè)函數(shù)的作用域鏈中都保存著函數(shù)的活動(dòng)對(duì)象,所以他們引用的是同一個(gè)變量i,當(dāng)函數(shù)返回后,變量i的值為10。

或許只是知道了這個(gè)原理還不能夠理解為什么每個(gè)函數(shù)都返回10,那么你就有可能要了解函數(shù)(這里指的是匿名函數(shù))的執(zhí)行時(shí)刻了:在上述函數(shù)中的匿名函數(shù)在定義函數(shù)體之后不會(huì)主動(dòng)執(zhí)行,所以此時(shí)的rusult返回了[function (){return i;},...,function (){return i;}] 數(shù)組里面包含10個(gè)函數(shù)表達(dá)式,而不是匿名函數(shù)表達(dá)式執(zhí)行之后的返回值組成的數(shù)組;而arr()執(zhí)行時(shí),此時(shí)i的值是10,所以每個(gè)匿名函數(shù)表達(dá)式執(zhí)行后都會(huì)返回10。
先來了解一下函數(shù)聲明、函數(shù)表達(dá)式和匿名函數(shù)

函數(shù)聲明:function fnName () {…};使用function關(guān)鍵字聲明一個(gè)函數(shù),再指定一個(gè)函數(shù)名,叫函數(shù)聲明。

函數(shù)表達(dá)式:var fnName = function () {…};使用function關(guān)鍵字聲明一個(gè)函數(shù),但未給函數(shù)命名,最后將匿名函數(shù)賦予一個(gè)變量,叫函數(shù)表達(dá)式,這是最常見的函數(shù)表達(dá)式語法形式。

匿名函數(shù):function () {}; 使用function關(guān)鍵字聲明一個(gè)函數(shù),但未給函數(shù)命名,所以叫匿名函數(shù),匿名函數(shù)屬于函數(shù)表達(dá)式!函數(shù)有很多作用,賦予一個(gè)變量則創(chuàng)建函數(shù),賦予一個(gè)事件則成為事件處理程序或創(chuàng)建閉包等等。

函數(shù)聲明和函數(shù)表達(dá)式不同之處在于:

Javascript引擎在解析javascript代碼時(shí)會(huì)‘函數(shù)聲明提升"(Function declaration Hoisting)當(dāng)前執(zhí)行環(huán)境(作用域)上的函數(shù)聲明,而函數(shù)表達(dá)式必須等到Javascirtp引擎執(zhí)行到它所在行時(shí),才會(huì)從上而下一行一行地解析函數(shù)表達(dá)式
而且表達(dá)式后面可以加括號(hào)立即調(diào)用該函數(shù),函數(shù)聲明不可以,只能以fnName()形式調(diào)用
具體內(nèi)容請(qǐng)查看有關(guān)函數(shù)的章節(jié)。
接下來我們來學(xué)習(xí)如何讓匿名函數(shù)立即執(zhí)行,來達(dá)到預(yù)期的目的:
function createFunctions(){

    var result=new Array();
    for(var i=0;i<10;i++){
        result[i]=( function(){
            return i;
            }) ();   
    }
    return result;
}
var arr=createFunctions();
alert(arr);//[0,1,2,……,9]

(function(){return i; }());可以讓匿名函數(shù)表達(dá)式立即執(zhí)行,此時(shí)result[index]里面存的就是匿名函數(shù)立即執(zhí)行后的返回值。
下面貼出立即調(diào)用的函數(shù)表達(dá)式的幾種方法(PS:雖然我只試過前三個(gè)):

1.最流行的寫法:(function(){// do something })();
2.[function(){// do something }()];
3.(function(){// do something }());
4.!function(){ // do something }();
5.~ function() {}();
6.+ function() {}();
7.- function() {}()

8.delete function() {}();
9.typeof function() {}();
10.void function() {}();
11.new function() {}();
12.new function() {};

13.var f = function() {}();
14.1, function() {}();
15.1 ^ function() {}();
16.1 > function() {}();

注:有些人的確會(huì)用「自執(zhí)行的匿名函數(shù)」(self-executing anonymous function)這個(gè)術(shù)語,但是 Ben Alman 推薦了一個(gè)更準(zhǔn)確的叫法:「立即調(diào)用的函數(shù)表達(dá)式」(IIFE,Immediately-Invoked Function Expression——雖然更長(zhǎng)了點(diǎn),但是這個(gè)術(shù)語,縮寫漂亮,含義更清晰。
function(){// do something }()會(huì)提示語法錯(cuò)誤:

function(){}()并不是合法的EcmaScript字符串,因?yàn)?JavaScript 文法明確規(guī)定表達(dá)式語句不得以 function 或者 { 開頭。而“!function(){}()”是,們從規(guī)范出發(fā)。

閉包的用途:
閉包可以用在許多地方,它的最大用處有兩個(gè):

一個(gè)是可以讀取函數(shù)內(nèi)部的變量

另一個(gè)就是讓這些變量的值始終保持在內(nèi)存中。

怎么來理解這句話呢?請(qǐng)看下面的代碼。

 

function f1(){
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000

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

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

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

希望一個(gè)變量長(zhǎng)期駐扎在內(nèi)存當(dāng)中

避免全局變量的污染

私有成員的存在

閉包的用法:

模塊化代碼

在循環(huán)中直接找到對(duì)應(yīng)元素的索引

注:函數(shù)嵌套函數(shù),內(nèi)部函數(shù)可以引用外部函數(shù)的參數(shù)和變量,參數(shù)和變量不會(huì)被垃圾回收機(jī)制所收回:

執(zhí)行say667()后,say667()閉包內(nèi)部變量會(huì)存在,而閉包內(nèi)部函數(shù)的內(nèi)部變量不會(huì)存在.使得Javascript的垃圾回收機(jī)制GC不會(huì)收回say667()所占用的資源,因?yàn)閟ay667()的內(nèi)部函數(shù)的執(zhí)行需要依賴say667()中的變量。這是對(duì)閉包作用的非常直白的描述.
  function say667() {
    // Local variable that ends up within closure
    var num = 666;
    var sayAlert = function() { alert(num); }
    num++;
    return sayAlert;
}
 var sayAlert = say667();
 sayAlert()//執(zhí)行結(jié)果應(yīng)該彈出的667

問題求解:
function createFunctions(){

    var result=new Array();
    for(var i=0;i<10;i++){
        result[i]= function(){
            return i;
            }();   
    }
    return result;
}
var arr=createFunctions();
alert(arr);//[0,1,2,……,9]

我想立即調(diào)用匿名函數(shù)來給result[index]賦值,網(wǎng)上查閱有幾種方法是:

1.!function(){ // do something }();
2.~ function() {}();
3.+ function() {}();
4.- function() {}()

有人說function(){return i; }();會(huì)提示語法錯(cuò)誤
實(shí)際情況是function(){return i; }();并沒有提示錯(cuò)誤。而+-!~會(huì)改變匿名函數(shù)返回值
回答:具體應(yīng)該說是result= function(){//dosomething}();不會(huì)有語法錯(cuò)誤吧,function() {//dosomething}();多帶帶執(zhí)行還是有語法錯(cuò)誤的。
result=
1.function(){//dosomething}();
2.!function(){ // do something }();
3.~ function() {}();
4.+ function() {}();
5.- function() {}()

原理是語句function(){//dosomething}()都被當(dāng)作表達(dá)式處理了(賦值前計(jì)算表達(dá)式的值),就是2、3、4、5例可以自執(zhí)行,自執(zhí)行的原因也是function(){//dosomething}()由于前面的符號(hào)被當(dāng)作表達(dá)式處理了。
至于result= function(){//dosomething}();不會(huì)有語法錯(cuò)誤就是因?yàn)橘x值語句把右邊的function(){//dosomething}()當(dāng)作表達(dá)式處理了,然后將結(jié)果賦給result。

var someFunction=function(){
//這里是塊級(jí)及作用域
};
someFunction();
這里定義了一個(gè)函數(shù)(匿名函數(shù),并把值賦給someFunction)然后立即調(diào)用了它(在函數(shù)名稱后面添加一對(duì)括號(hào))。

function(){
//這里是塊及作用域
}();
這段代碼會(huì)導(dǎo)致語法錯(cuò)誤,是因?yàn)閖avascript將function關(guān)鍵字當(dāng)作一個(gè)函數(shù)聲明的開始,而函數(shù)聲明后面不能跟圓括號(hào)。而表達(dá)式后面可以跟圓括號(hào),要將函數(shù)聲明變成表達(dá)式,只要向下面那樣給它加一對(duì)圓括號(hào)即可:

(function(){
//這里是塊及作用域
})();

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/79445.html

相關(guān)文章

  • 詳解js閉包

    摘要:但閉包的情況不同嵌套函數(shù)的閉包執(zhí)行后,,然后還在被回收閉包會(huì)使變量始終保存在內(nèi)存中,如果不當(dāng)使用會(huì)增大內(nèi)存消耗。每個(gè)函數(shù),不論多深,都可以認(rèn)為是全局的子作用域,可以理解為閉包。 閉包(closure)是Javascript語言的一個(gè)難點(diǎn),也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)。 閉包的特性 閉包有三個(gè)特性: 1.函數(shù)嵌套函數(shù) 2.函數(shù)內(nèi)部可以引用外部的參數(shù)和變量 3.參數(shù)和變量不會(huì)...

    Chiclaim 評(píng)論0 收藏0
  • 閉包詳解

    摘要:再看一段代碼這樣就清晰地展示了閉包的詞法作用域能訪問的作用域?qū)?dāng)做一個(gè)值返回執(zhí)行后,將的引用賦值給執(zhí)行,輸出了變量我們知道通過引用的關(guān)系,就是函數(shù)本身。 在正式學(xué)習(xí)閉包之前,請(qǐng)各位同學(xué)一定要確保自己對(duì)詞法作用域已經(jīng)非常的熟悉了,如果對(duì)詞法作用域還不夠熟悉的話,可以先看: 深入理解閉包之前置知識(shí)---作用域與詞法作用域 前言 現(xiàn)在去面試前端開發(fā)的崗位,如果你的面試官也是個(gè)前端,并且不是太...

    cnio 評(píng)論0 收藏0
  • JavaScript 中 閉包詳解

    摘要:局部變量,當(dāng)定義該變量的函數(shù)調(diào)用結(jié)束時(shí),該變量就會(huì)被垃圾回收機(jī)制回收而銷毀。如果在函數(shù)中不使用匿名函數(shù)創(chuàng)建閉包,而是通過引用一個(gè)外部函數(shù),也不會(huì)出現(xiàn)循環(huán)引用的問題。 閉包是什么 在 JavaScript 中,閉包是一個(gè)讓人很難弄懂的概念。ECMAScript 中給閉包的定義是:閉包,指的是詞法表示包括不被計(jì)算的變量的函數(shù),也就是說,函數(shù)可以使用函數(shù)之外定義的變量。 是不是看完這個(gè)定義感...

    longshengwang 評(píng)論0 收藏0
  • Javascript 閉包詳解

    摘要:一般函數(shù)執(zhí)行完畢,局部活動(dòng)對(duì)象就會(huì)被銷毀,內(nèi)存中僅僅保存全局作用域,但是閉包會(huì)長(zhǎng)期駐扎在內(nèi)存。我只是想通過這兩個(gè)例子來說明閉包的用處和好處。閉包會(huì)使變量始終保存在內(nèi)存中,如果使用不當(dāng)會(huì)增大內(nèi)存消耗。 閉包特性 函數(shù)嵌套函數(shù) 函數(shù)內(nèi)部可以引用外部的參數(shù)和變量 參數(shù)和變量不會(huì)被垃圾回收機(jī)制回收 閉包的作用 具體作用是有權(quán)訪問函數(shù)內(nèi)部的變量,最常見的就是函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù),通過另一個(gè)函數(shù)...

    ztyzz 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<