午夜精品福利视频,亚洲激情专区,免费看a网站,aa毛片,亚洲色图激情小说,亚洲一级毛片,免费一级毛片一级毛片aa

淺談js之閉包 -電腦資料

電腦資料 時(shí)間:2019-01-01 我要投稿
【m.stanzs.com - 電腦資料】

    "官方"的解釋是指一個(gè)擁有許多變量和綁定了這些變量的環(huán)境的表達(dá)式(通常是一個(gè)函數(shù)),因而這些變量也是該表達(dá)式的一部分;

    紅皮書是這樣說的,閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中變量的函數(shù);常見的創(chuàng)建閉包的方式就是在一個(gè)函數(shù)中再創(chuàng)建一個(gè)函數(shù);

    閉包是一種特殊的對象,

淺談js之閉包

。它由兩部分構(gòu)成:函數(shù),以及創(chuàng)建該函數(shù)的環(huán)境。環(huán)境由閉包創(chuàng)建時(shí)在作用域中的任何局部變量組成;

    光看定義是云里霧里,但是到了真正的代碼了又是什么樣的形式呢?經(jīng)典的閉包例子:

    function fn() {

    var name = 4;

    return function () {

    var n = 0;

    alert(++n);

    alert(++name);

    }

    }

    var fun = fn();

    fun();//n =>1,name=>5;

    fun();// n =>1,name=>6;

    這里就有閉包產(chǎn)生了,fun就是閉包;這個(gè)閉包由fn中的匿名函數(shù)和name變量構(gòu)成。,但是呢,它又是一種特殊的函數(shù),它是一種能能夠讀取其他函數(shù)內(nèi)部變量的特殊函數(shù);fn中的匿名函數(shù)的父函數(shù)在執(zhí)行完了之后,按正常來說,它應(yīng)該被銷毀,里面的變量也被銷毀,,但是里面的變量現(xiàn)在沒有被銷毀,而是被這個(gè)匿名函數(shù)引用著;(說實(shí)在的它不應(yīng)該被銷毀,因?yàn)檫@個(gè)匿名函數(shù)還沒有執(zhí)行,還要用上一級的函數(shù)的中的變量,你給我銷毀了,我可怎么辦,那不是讓我報(bào)錯(cuò)呀,但是呢,我給你用,不銷毀,這又不符合規(guī)矩,按規(guī)矩是這樣的:當(dāng)一個(gè)函數(shù)執(zhí)行完之后,是要被立即銷毀的,執(zhí)行環(huán)境銷毀,里面的變量銷毀,你現(xiàn)在不讓我銷毀,那不亂套了,那怎么辦呢,于是乎,一群磚家,就說趕這種方式叫閉包吧)意思是說我跟你們不一樣;因?yàn)槭翘厥夂瘮?shù),代碼的最后一句fun()執(zhí)行完之后,name變量還是沒有釋放,但是每次執(zhí)行fun,里面的n變量是都是新創(chuàng)建的,執(zhí)行完之后又釋放掉;要是你明白了就不需要看括號里的內(nèi)容了(這里我就形象的說為什么說name變量會一直在內(nèi)存中?在剛開始的時(shí)候,父函數(shù)fn在剛要執(zhí)行完了,開始銷毀時(shí),匿名子函數(shù)就說了,我要用你的name變量,你先別銷毀了,父函數(shù)說好吧,于是乎,父函數(shù)執(zhí)行完之后(歸天了),就沒有銷毀name,在當(dāng)你調(diào)用fun,執(zhí)行匿名子函數(shù),fun()調(diào)用完了,你把自己家的n變量銷毀了,fun就說了name又不是我的東西,我就是用了一下,憑什么我給銷毀,我不給銷毀,但是這時(shí)父函數(shù)已經(jīng)去世了(執(zhí)行完了),于是就產(chǎn)生了內(nèi)存消耗,除非你手動(dòng)銷毀,垃圾收回機(jī)制不會自動(dòng)收回;這又牽扯到內(nèi)存泄漏,性能問題了,后面說。)

    作用域鏈:

    講到這里,如果要想整整的明白還有知道作用域鏈,和垃圾收回機(jī)制;

    我就說說上面代碼執(zhí)行時(shí)的作用域鏈:

    我就說說這張圖是什么意思,這種圖是執(zhí)行var fun = fu();fun();這兩句代碼時(shí)所發(fā)生的情況;

    其實(shí)匿名函數(shù)在fu()被返回時(shí),它的作用域鏈就被初始化為包含全局變量對象和fu函數(shù)的活動(dòng)對象;也就是說當(dāng)fu函數(shù)執(zhí)行完返回后,它的執(zhí)行環(huán)境會被銷毀,但是其活動(dòng)對象不會被銷毀,仍然在內(nèi)存中,因?yàn)槟涿瘮?shù)的作用域鏈中引用了這個(gè)活動(dòng)對象。只有到匿名函數(shù)被手動(dòng)銷毀時(shí)才銷毀;其實(shí)在fu執(zhí)行完后,紅字顯示的部分就消失了,就活動(dòng)變量沒有消失;

    再說一點(diǎn)關(guān)于作用域鏈的問題:

    1。作用域鏈中的變量對象(函數(shù)中叫活動(dòng)對象)保存的是變量和函數(shù);

    2.作用域鏈的作用就是為了保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。

    3.查找一個(gè)變量是從作用域鏈的最前端,逐漸向上找,只要找到就不再向上找了,不論上面是否還有這個(gè)值;

    4.就以上面的fu函數(shù)為例吧,在聲明fu函數(shù)的時(shí)候就開始預(yù)先創(chuàng)建一個(gè)包含全局變量對象的作用域鏈了(如果嵌套多了,其實(shí)就是在函數(shù)聲明的地方創(chuàng)建父函數(shù)及其之上的作用域鏈),這個(gè)作用域鏈將被保存在剛創(chuàng)建函數(shù)的內(nèi)部[[Scope]]的屬性中;當(dāng)調(diào)用fu函數(shù)時(shí),會為函數(shù)創(chuàng)建一個(gè)執(zhí)行環(huán)境,然后通過復(fù)制函數(shù)的[[Scope]]中的作用域鏈構(gòu)建起執(zhí)行環(huán)境的作用域鏈;之后還要?jiǎng)?chuàng)建一個(gè)本函數(shù)的活動(dòng)對象,并把這個(gè)活動(dòng)對象推入執(zhí)行環(huán)境作用域鏈的前端。

    5.作用域鏈本質(zhì)上就是一個(gè)指向變量對象的指針列表。

    垃圾回收機(jī)制:

    再說說垃圾回收機(jī)制:

    1.如果一個(gè)對象不再有引用了,這個(gè)對象就會被GC收回;

    2,如果兩個(gè)對象互相引用,但不被第三個(gè)引用,這兩個(gè)互相引用的對象也會收回的。

    而閉包不再這個(gè)范疇之內(nèi)。

    閉包的特性:

    1.引用的變量不被垃圾回收機(jī)制收回

    2.函數(shù)內(nèi)部可以引用外部的變量;

    3.函數(shù)里面嵌套函數(shù)

    閉包的用處(好處):

    1.私有變量和方法

    var a=(function() {

    var privateNum = 1;

    function privateFun(val) {

    alert(val);

    }

    return {

    publicFun: function() {

    privateFun(2);

    },

    publicNum:function() {

    return privateNum;

    }

    }

    })();

    a.publicNum();//1

    a.publicFun();//2

    如果你用a.privateNum,a.privateFun();這是會報(bào)錯(cuò)的。

    2.實(shí)現(xiàn)一些變量的累加

    function a() {

    var n=0;

    return function () {

    n++;

    alert(n);

    }

    }

    var b = a();

    b();//1

    b();//2

    b();//3

    這里只是要使用累加,就這樣干,具體還要具體分析,原理是這樣了

    因閉包產(chǎn)生的問題

    初學(xué)者常見的,循環(huán)閉包

    大部分我們所寫的 Web JavaScript. 代碼都是事件驅(qū)動(dòng)的 — 定義某種行為,然后將其添加到用戶觸發(fā)的事件之上(比如點(diǎn)擊或者按鍵),

電腦資料

淺談js之閉包》(http://m.stanzs.com)。我們的代碼通常添加為回調(diào):響應(yīng)事件而執(zhí)行的函數(shù)。

    閉包循環(huán)問題

    你不管點(diǎn)擊哪一個(gè),都alert”我是5號“;

    原因就是你循環(huán)了五次產(chǎn)生了五個(gè)閉包,而這5個(gè)閉包共享一個(gè)變量i,說的明白一點(diǎn)就是,在for循環(huán)結(jié)束時(shí),只是把這五個(gè)匿名函數(shù)注冊給click事件,當(dāng)時(shí)在循環(huán)的時(shí)候并沒有執(zhí)行,當(dāng)循環(huán)結(jié)束了,此時(shí)i的值是5;之后你去點(diǎn)擊p標(biāo)簽,你點(diǎn)擊哪一個(gè)就執(zhí)行哪一個(gè)對應(yīng)的匿名函數(shù)(這個(gè)時(shí)候才執(zhí)行),這時(shí)候匿名中發(fā)現(xiàn)一個(gè)i,匿名中沒有定義i,于是沿著作用域鏈找,找到了,但是這時(shí)候循環(huán)早就結(jié)束了,i等于5,于是彈出”我是5號“來;點(diǎn)擊其他的同理;

    怎么解決呢:

    一種方法是再創(chuàng)建一個(gè)閉包,把js代碼改為這樣就行了

    var page = document.getElementsByTagName("p");

    for(var i=0; i

    !function(num) {

    page[i].onclick = function () {

    alert("我是"+num+"號");

    }

    }(i)

    }

    我只說一點(diǎn),這次五個(gè)閉包不共享num,而是創(chuàng)建五個(gè)num變量

    還有一種解決方式:

    var page = document.getElementsByTagName("p");

    for(var i=0; i

    page[i].num = i//先把每個(gè)變量值存起來

    page[i].onclick = function () {

    alert("我是"+this.num+"號");

    }

    }

    閉包中的this對象

    var num = 1;

    var bj = {

    num:2,

    getNum:function() {

    return function () {

    return this.num;

    }

    }

    }

    alert(obj.getNum()());//num -> 1

    為什么不彈出2呢,這里是說明閉包中你需要注意現(xiàn)在的this的指向那一個(gè)對象,其實(shí)記住一句話就永遠(yuǎn)不會用錯(cuò)this的指向問題,this永遠(yuǎn)指向調(diào)用它的作用域;

    如果這樣寫你就可能理解了

    var num = 1;

    var bj = {

    num:2,

    getNum:function() {

    return function () {

    return this.num;

    }

    }

    }

    var a = obj.getNum();

    alert(window.a());//1

    其實(shí)是window對象調(diào)用的,這就是說閉包中的this讓你看不清this的指向;

    要是讓它alert 2你要這樣:

    var num = 1;

    var bj = {

    num:2,

    getNum:function() {

    var _this = this;//在這里保存this

    return function () {

    return _this.num;

    }

    }

    }

    var a = obj.getNum();

    alert(window.a());

    性能考量

    如果不是因?yàn)槟承┨厥馊蝿?wù)而需要閉包,在沒有必要的情況下,在其它函數(shù)中創(chuàng)建函數(shù)是不明智的,因?yàn)殚]包對腳本性能具有負(fù)面影響,包括處理速度和內(nèi)存消耗。

    例如,在創(chuàng)建新的對象或者類時(shí),方法通常應(yīng)該關(guān)聯(lián)于對象的原型,而不是定義到對象的構(gòu)造器中。原因是這將導(dǎo)致每次構(gòu)造器被調(diào)用,方法都會被重新賦值一次(也就是說,為每一個(gè)對象的創(chuàng)建)。

    function MyObject(name, message) {

    this.name = name.toString();

    this.message = message.toString();

    this.getName = function() {

    return this.name;

    };

    this.getMessage = function() {

    return this.message;

    };

    }

    應(yīng)該是這樣

    function MyObject(name, message) {

    this.name = name.toString();

    this.message = message.toString();

    }

    MyObject.prototype.getName = function() {

    return this.name;

    };

    MyObject.prototype.getMessage = function() {

    return this.message;

    };

    示例中,繼承的原型可以為所有對象共享,且不必在每一次創(chuàng)建對象時(shí)定義方法

最新文章