国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

請(qǐng)用一句優(yōu)雅的話表達(dá)javascript閉包

mochixuan / 1001人閱讀

摘要:局部變量只在當(dāng)前函數(shù)體內(nèi)有效,出了函數(shù)體,就上一級(jí)的范圍,局部變量無效。但是在中,函數(shù)內(nèi)部有一個(gè)函數(shù),它的函數(shù)體內(nèi)的是指中聲明的局部變量,而非全局變量。這就是一個(gè)非常典型的閉包了。

嚴(yán)格的講,閉包常常表現(xiàn)為一個(gè)函數(shù)內(nèi)部的函數(shù),它使用了非自己定義的、自己所在作用域內(nèi)的變量,并且使這些變量突破了作用域的限制。

函數(shù)內(nèi)部的函數(shù):私有函數(shù)

首先,我們從這個(gè)內(nèi)部函數(shù)去說開,因?yàn)檫@個(gè)是形式上的,如果一開始講作用域,有點(diǎn)故意。閉包在形式上就是函數(shù)內(nèi)部的函數(shù),比如:

jQuery(function($){ 
    function message(msg) { 
        alert(msg); 
    } 
 
    if($(window).width() > 1000) message("window寬度大于1000"); 
});

如果你用jquery,這段代碼應(yīng)該經(jīng)常使用吧。如果你仔細(xì)去觀察,就會(huì)發(fā)現(xiàn),第一個(gè)function被作為參數(shù),傳給了jQuery()這個(gè)函數(shù),而在function內(nèi),又有一個(gè)message()函數(shù)。所有的jQuery代碼被放在第一個(gè)function中去處理。第二個(gè)函數(shù)就是函數(shù)體內(nèi)部的函數(shù),這個(gè)函數(shù)在函數(shù)體內(nèi)聲明,一旦外層函數(shù)執(zhí)行完畢,那么這個(gè)函數(shù)就失去了作用,在jQuery()外無法使用message(),因此,message()是第一個(gè)函數(shù)內(nèi)部的私有函數(shù)。

變量的作用域

函數(shù)內(nèi)部的變量有兩種,一種是局部變量,一種是全局變量。局部變量只在當(dāng)前函數(shù)體內(nèi)有效,出了函數(shù)體,就上一級(jí)的范圍,局部變量無效。

var age = 10; 
function a(age) { 
    return age + 1; 
} 
function b(_age) { 
    return age + _age; 
} 
function c(_age) { 
    var age = 11; 
    function add() { 
        return age + _age; 
    } 
    return add(); 
} 
 
alert(a(9)); // 10 : 9 + 1 
alert(b(2)); // 12 : 10 + 2 
alert(c(5)); // 16 : 11 + 5

在上面的代碼中,我們看b和c函數(shù)。b函數(shù)中的age直接引用了全局變量age(10),而c函數(shù)中重新聲明了局部變量age,因此,全局變量age在c函數(shù)中無效。

但是在c中,函數(shù)內(nèi)部有一個(gè)函數(shù)add(),它的函數(shù)體內(nèi)的age是指c中聲明的局部變量,而非全局變量age。從這個(gè)例子里,反映出了變量的作用域,函數(shù)內(nèi)的函數(shù)體里,如果沒有聲明局部變量,就會(huì)承認(rèn)其父級(jí)甚至祖先級(jí)函數(shù)的變量,以及全局變量。

閉包

怎么樣才算是一個(gè)閉包呢?我們來看下面的代碼:

function a() { 
    var age = 10; 
    return function(){ 
        return age; 
    } 
} 
 
var age = a(); 
alert(age()); // 10

按照我們前面說的作用域,在上面這段代碼中age是a()的局部變量,按道理,出了函數(shù),就不能被訪問了。但是,a()返回了一個(gè)私有函數(shù),個(gè)這個(gè)函數(shù)返回了age,這導(dǎo)致我們可以在a()外部,仍然可以訪問到本來是局部變量的age,這個(gè)時(shí)候,我們就把這個(gè)內(nèi)部函數(shù)稱為閉包。它的原理就是,函數(shù)內(nèi)部的函數(shù),可以訪問父函數(shù)的局部變量。

綜合上面的闡述,我們要這樣去理解閉包:

閉包是一個(gè)函數(shù),它使用了自己之外的變量。

閉包是一個(gè)作用域。

閉包是“由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體”。

嚴(yán)格的講,閉包常常表現(xiàn)為一個(gè)函數(shù)內(nèi)部的函數(shù),它使用了非自己定義的、自己所在作用域內(nèi)的變量,并且使這些變量突破了作用域的限制。

一個(gè)典型的閉包:

函數(shù)內(nèi)的函數(shù)

這個(gè)內(nèi)部函數(shù)引用了父函數(shù)的局部變量

這個(gè)內(nèi)部函數(shù)使引用的變量突破了作用域限制

var a = 1;
function fun() {return a + 1;}
alert(fun());

這也可以算作一個(gè)閉包,a()引用了它之外定義的變量。但是這不算嚴(yán)格的閉包,因?yàn)樗鼪]有在突破作用域的這個(gè)點(diǎn)上表現(xiàn)出來。

var a = 1; 
function fun() { 
    var b = 2; 
    return function(){ 
        return a + ++b; 
    }; 
} 
var c = fun(); 
alert(c()); // 4 
alert(c()); // 5

這就是一個(gè)非常典型的閉包了。而且為什么alert(c())兩次的值不同,我們還會(huì)在下面解釋到。

為了讓你更加明晰的和你曾經(jīng)開發(fā)過的案例聯(lián)系在一起,我們來看我們?cè)?jīng)做過的這樣的事:

define(function(){ 
    var age = 10; 
 
    function getAge() { 
        return age; 
    } 
    function grow() { 
        age ++; 
    } 
 
    return { 
        age : getAge, 
        grow : grow 
    }; 
});

這是我們?cè)趓equire.js中的一種寫法,把它還原為我們熟悉的閉包模式:

function Cat(){ 
    var age = 10; 
 
    function getAge() { 
        return age; 
    } 
    function grow() { 
        age ++; 
    } 
 
    return { 
        ageAge : getAge, 
        grow : grow 
    }; 
}; 
var cat = Cat(); 
var age = cat.getAge(); 
alert(age); // 10 
cat.grow(); 
age = cat.getAge(); 
alert(age); // 11

從內(nèi)存看閉包

現(xiàn)在,我們就要來解釋為什么上面的alert()兩次的結(jié)果不同的原因了。

首先,我們來看下普通的函數(shù)聲明和使用過程中內(nèi)存的變化:

function fun(a,b) { 
  return a+b; 
} 
alert(fun(1,2)); 
alert(fun(3,4));

上面是我們沒有遇到閉包的情況,內(nèi)存我們這樣來畫(注意,我這里只是抽象的畫出內(nèi)存變化,而不是真實(shí)的javascript內(nèi)存機(jī)制。)

【圖片缺失,請(qǐng)查看文末的原文鏈接,原文中圖片正常】

在每一次執(zhí)行完fun()之后,fun()函數(shù)的執(zhí)行環(huán)境被釋放(回收機(jī)制)。

接下來我們來看一個(gè)閉包:

function fun(a) { 
    return function(b){return a + b;} 
} 
var add = fun(2); 
alert(add(2)); 
alert(add(4));

上面就出現(xiàn)閉包了,注意,我們這里出現(xiàn)了一個(gè)add變量。

【圖片缺失,請(qǐng)查看文末的原文鏈接,原文中圖片正常】

在后兩步中,實(shí)際上fun(2)部分沒有任何變化,所變的,則是在內(nèi)部函數(shù)所對(duì)應(yīng)的內(nèi)存區(qū)域中有變化。細(xì)心的你,可能會(huì)發(fā)現(xiàn)這里面的問題所在,當(dāng)執(zhí)行完add(2)之后,fun對(duì)應(yīng)的內(nèi)存沒有被釋放掉,而它的內(nèi)部函數(shù),也就是function(2)被釋放掉了,在執(zhí)行add(4)的時(shí)候,僅僅重新運(yùn)行了內(nèi)部函數(shù)。如果連fun()對(duì)應(yīng)的內(nèi)存出現(xiàn)了變化怎么辦?我們來看下面的例子:

function fun() { 
    var b = 2; 
    return function(a){ 
        return a + ++b; 
    }; 
} 
var c = fun(); 
alert(c(1)); // 4 
alert(c(1)); // 5

注意,這可是個(gè)非常典型的閉包的例子,它有一個(gè)局部變量b,我們來看它的內(nèi)存圖。

【圖片缺失,請(qǐng)查看文末的原文鏈接,原文中圖片正常】

注意第2、3、4步中內(nèi)存的變化。第2步時(shí),我們僅僅將fun()賦給變量c,這個(gè)時(shí)候,內(nèi)部函數(shù)function(a)并沒有被執(zhí)行,所以++b也沒有被執(zhí)行,b的值還是2。但是第3步開始,++b被先執(zhí)行,++b的意思是先自加,再進(jìn)行運(yùn)算,和b++是不同的,如果是b++,雖然c(1)的最終結(jié)果還是為4,但是在c(1)執(zhí)行開始時(shí),b應(yīng)該為2,執(zhí)行完之后才是3。

奇妙的事情發(fā)生了,在內(nèi)部函數(shù)中,++b導(dǎo)致了局部變量值發(fā)生了變化,b從2變成了3,而且,內(nèi)存并沒有被釋放,fun()的執(zhí)行環(huán)境沒有被銷毀,b還被保存在內(nèi)存中。到第4步時(shí),b的初始值是3,經(jīng)過++b 之后,變成了4。

這個(gè)內(nèi)存分析非常形象的把閉包概念中,關(guān)于“突破作用域限制”這個(gè)點(diǎn)描述的非常清楚,原本按照作用域的限制,函數(shù)的局部變量不能被外部環(huán)境訪問,更不能被修改,但是閉包卻使得外部環(huán)境不僅可以讀取到局部變量的內(nèi)容,甚至可以修改它,深入一點(diǎn)就是:閉包會(huì)導(dǎo)致閉包函數(shù)所涉及到的非自身定義的變量一直保存在內(nèi)存中,包括其父函數(shù)在內(nèi)的相關(guān)環(huán)境都不會(huì)被銷毀

閉包到底有什么用?

說了這么多,那閉包到底有什么用,我們?yōu)槭裁匆褂瞄]包呢?從上面的闡述中,你應(yīng)該已經(jīng)知道了閉包的唯一作用:突破作用域限制。那如何使用這個(gè)作用為程序服務(wù)呢?

常駐內(nèi)存,意味著讀取速度快(,當(dāng)然,內(nèi)存花銷也大,導(dǎo)致內(nèi)存溢出)。常駐內(nèi)存,意味著一旦初始化以后,就可以反復(fù)使用同一個(gè)內(nèi)存中的某個(gè)對(duì)象,而無需再次運(yùn)行程序。而這一點(diǎn),是很多插件、模塊的設(shè)計(jì)思想。

最好的例子就是上文我舉得那個(gè)define()的例子,后面用我們今天所了解的形式去實(shí)踐之后,你就會(huì)發(fā)現(xiàn)原來可以把function當(dāng)做一個(gè)其他語言中的class來對(duì)待,cat.getAge(), cat.grow()這樣的操作是不是很符合我們?cè)诰幊讨械氖褂昧?xí)慣呢?一旦一個(gè)產(chǎn)生之后,這個(gè)cat就一直在內(nèi)存中,隨時(shí)可以拿出來就用,它就是一個(gè)實(shí)例化對(duì)象。

為了更形象,我們來創(chuàng)建一個(gè)簡(jiǎn)單的代碼塊:

function Animal() { 
    this.age = 1; 
    this.weight = 10; 
    return { 
        getAge : function(){ 
            return this.age; 
        }, 
        getWeight : function(){ 
            return this.weight; 
        }, 
        grow : function(){ 
             this.age ++; 
             this.weight = this.age * 10 * 0.8; 
        } 
    }; 
} 
function Cat() { 
    var cat = new Animal(); // 繼承 
    cat.grow = function(){ 
        cat.age ++; 
        cat.weight = cat.age * 10 * 0.6; 
    } 
    return cat; 
} 
 
var cat1 = new Cat(); 
alert(cat1.getAge()); 
cat1.grow(); 
alert(cat1.getAge());

為什么要舉這個(gè)例子呢,因?yàn)槲蚁胱屇阆胂筮@樣一種場(chǎng)景,如果沒有閉包怎么辦?

沒有閉包是這樣的一種狀態(tài):函數(shù)無法訪問自己父級(jí)(祖先,全局)對(duì)象的變量。比如:

var a = 1; 
function add() { 
    return ++a; // 如果沒有閉包機(jī)制,會(huì)undefined報(bào)錯(cuò) 
}

這種情況怎么辦?必須以參數(shù)的形式傳入到函數(shù)中:

var a = 1; 
function add(a) { 
    return ++a; 
} 
alert(add(a)); // 2

如果是這樣,就很麻煩了,你需要在每一個(gè)函數(shù)中傳入變量。而更麻煩的是,沒有了作用域的突破,例如:

function Cat() { 
    age = 1; 
    function getAge(age) { 
        return age; 
    } 
    function grow(age) { 
        age ++; 
    } 
    return { 
        getAge : getAge, 
        grow : grow 
    } 
} 
var cat = new Cat(); 
cat.grow(); 
alert(cat.getAge()); // 1,沒有被修改

這種情況下,我們無論如何都無法使用這種辦法來實(shí)現(xiàn)我們的目的。唯一能夠?qū)崿F(xiàn)的,就是按照下面這種方法:

var cat = { 
    age : 1, 
    weight : 10, 
    grow : function(){ 
        this.age ++; 
        this.weight += 3; 
    } 
}; 
var cat1 = cat; 
alert(cat1.age); 
cat1.grow(); 
alert(cat1.age);

我們聰明的使用到了this關(guān)鍵字,但是這樣一個(gè)壞處是,age, weight這些屬性直接暴露給外部,我們只需要執(zhí)行 cat1.age = 12; 就可以馬上讓cat長(zhǎng)到12歲,而體重卻沒任何變化。

總結(jié)而言,閉包可以帶來這么幾個(gè)方面的應(yīng)用優(yōu)勢(shì):

常駐內(nèi)存,加快運(yùn)行速度

封裝

閉包使用中的注意點(diǎn)

除了上面提到的內(nèi)存開銷問題外,還有一個(gè)需要被注意的地方,就是閉包所引用的外部變量,在一些特殊情況下會(huì)存在與期望值不同的誤差。

function init() {      
  var pAry = document.getElementsByTagName("p");      
  for( var i=0; i

在上面這段代碼中,你希望通過一個(gè)循環(huán),來為每一個(gè)p標(biāo)簽綁定一個(gè)click事件,然而不幸的是,for循環(huán)中使用的閉包函數(shù)沒有讓你如愿以償,在閉包函數(shù)中,i被認(rèn)作pAry.length,也就是循環(huán)到最后i的最終值。為什么會(huì)這樣呢? 原來,閉包引用變量,而非直接使用變量,“引用”的意思是將指針指向變量的內(nèi)容。由于這個(gè)原因,當(dāng)i=0的時(shí)候,閉包里面的i確實(shí)是0,但是當(dāng)隨著i的值變大的時(shí)候,閉包內(nèi)的i并沒有保存當(dāng)前值,而是繼續(xù)把指針指向i的內(nèi)容,當(dāng)你點(diǎn)擊某個(gè)p標(biāo)簽的時(shí)候,i的內(nèi)容實(shí)際上是for到最后i的值。

同樣,這個(gè)問題會(huì)出現(xiàn)在setTimeout,setInterval,$.ajax等這類操作中,你只要記住,當(dāng)你綁定操作時(shí),和執(zhí)行操作時(shí),對(duì)應(yīng)的變量是否已經(jīng)變化就OK了。

原文鏈接:http://www.tangshuang.net/2368.html

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/87745.html

相關(guān)文章

  • JS 中的閉包是什么?

    摘要:大名鼎鼎的閉包面試必問。閉包的作用是什么。看到閉包在哪了嗎閉包到底是什么五年前,我也被這個(gè)問題困擾,于是去搜了并總結(jié)下來。關(guān)于閉包的謠言閉包會(huì)造成內(nèi)存泄露錯(cuò)。閉包里面的變量明明就是我們需要的變量,憑什么說是內(nèi)存泄露這個(gè)謠言是如何來的因?yàn)椤? 本文為饑人谷講師方方原創(chuàng)文章,首發(fā)于 前端學(xué)習(xí)指南。 大名鼎鼎的閉包!面試必問。請(qǐng)用自己的話簡(jiǎn)述 什么是「閉包」。 「閉包」的作用是什么。 首先...

    Enlightenment 評(píng)論0 收藏0
  • JavaScript 編寫規(guī)范

    摘要:如果你想了解更多關(guān)于強(qiáng)制類型轉(zhuǎn)換的信息,你可以讀一讀的這篇文章。在只使用的情況下,所帶來的強(qiáng)制類型轉(zhuǎn)換使得判斷結(jié)果跟蹤變得復(fù)雜,下面的例子可以看出這樣的結(jié)果有多怪了明智地使用真假判斷當(dāng)我們?cè)谝粋€(gè)條件語句中使用變量或表達(dá)式時(shí),會(huì)做真假判斷。 說明 如果本文檔中有任何錯(cuò)誤的、不符合行規(guī)的,敬請(qǐng)斧正。 引言 不管有多少人共同參與同一項(xiàng)目,一定要確保每一行代碼都像是同一個(gè)人編寫的。...

    MartinDai 評(píng)論0 收藏0
  • Effective JavaScript讀書筆記(二)

    摘要:盡可能的使用局部變量,少用全局變量。正確的實(shí)現(xiàn)就是在函數(shù)體內(nèi)部使用將聲明成局部變量。在新特性中,引入了塊級(jí)作用域這個(gè)概念,因此還可以使用,來聲明局部變量。它們共享外部變量,并且閉包還可以更新的值。 變量作用域 作用域,對(duì)于JavaScript語言來說無處不在,變量作用域,函數(shù)作用域(運(yùn)行時(shí)上下文和定義時(shí)上下文),作用域污染等等都跟作用域息息相關(guān),掌握J(rèn)avaScript作用于規(guī)則,可以...

    Yuqi 評(píng)論0 收藏0
  • JavaScript之對(duì)象創(chuàng)建

    摘要:在構(gòu)造函數(shù)的內(nèi)部,的指向是新創(chuàng)建的對(duì)象。如果構(gòu)造函數(shù)沒有顯式的表達(dá)式,則會(huì)隱式的返回新創(chuàng)建的對(duì)象對(duì)象。原型模式在構(gòu)造函數(shù)模式中提到每次之后創(chuàng)建的新的對(duì)象是互相獨(dú)立的,是獨(dú)享的。 1.構(gòu)造函數(shù)模式 JavaScript中的構(gòu)造函數(shù)是通過new調(diào)用的,也就是說,通過new關(guān)鍵字調(diào)用的函數(shù)都被認(rèn)為是構(gòu)造函數(shù)。 在構(gòu)造函數(shù)的內(nèi)部,this的指向是新創(chuàng)建的對(duì)象Object。 如果構(gòu)造函數(shù)沒有顯式...

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

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

0條評(píng)論

mochixuan

|高級(jí)講師

TA的文章

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