成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

談js中的作用域鏈和閉包

LucasTwilight / 1498人閱讀

摘要:所以,當(dāng)在函數(shù)中使用全局變量的時(shí)候,所產(chǎn)生的代價(jià)是最大的,因?yàn)槿謱?duì)象一直處于作用域鏈的最末位置,讀取局部變量是最快的。

什么是作用域

在編程語(yǔ)言中,作用域控制著變量與參數(shù)的可見(jiàn)性及生命周期,它能減少名稱(chēng)沖突,而且提供了自動(dòng)內(nèi)存管理(javascript 語(yǔ)言精粹)

靜態(tài)作用域

再者,js不像其他的編程語(yǔ)言一樣,擁有著塊級(jí)作用域,就像下面一段代碼。

function afunction(){
    var a = "sf";
    console.log(b);
    console.log(c);
    var b = function(){
        console.log("這是b中的內(nèi)容");
    }
    function c(){
        console.log("這是c中的內(nèi)容");
    }
    (function d(){
        console.log("這是d中的內(nèi)容");
    })()
}

實(shí)用var聲明的變量和函數(shù)聲明將會(huì)進(jìn)行聲明提前afunction函數(shù)的執(zhí)行環(huán)境中,故上述代碼相當(dāng)于以下的代碼,在一個(gè)變量聲明提前的時(shí)候,其值為undefined,而函數(shù)聲明則是將函數(shù)體作為值。

function afunction(){
    var a;
    var b;
    function c(){
        console.log("這是c中的內(nèi)容");
    }
    a = "sf";
    console.log(b);
    console.log(c);
    b = function(){
        console.log("這是b中的內(nèi)容");
    }
    (function d(){
        console.log("這是d中的內(nèi)容");
    })()
}
全局作用域與局部作用域

將上述的代碼稍作改動(dòng)如下

var outer = "outer";
function afunction(){
    function c(){
        console.log("這是c中的內(nèi)容");
    }
    a = "sf";
    console.log(outer);
}

我們?cè)?b>afunction函數(shù)的外部定義了outer變量,假設(shè)這段代碼運(yùn)行在瀏覽器上,那么變量提前的過(guò)程中outer變量被聲明在了window作用域上,也就是瀏覽器中的全局作用域上,而函數(shù)中的變量則在函數(shù)運(yùn)行時(shí)被聲明在了afunction作用域上,這個(gè)就是局部作用域,在這個(gè)局部作用域中,outer變量被訪問(wèn)到了,這種跨作用域的讀取變量的形式就是根據(jù)作用域鏈來(lái)實(shí)現(xiàn)的。

什么是作用域鏈

在js中,函數(shù)也是對(duì)象,函數(shù)與其他的對(duì)象一樣,擁有可以訪問(wèn)的屬性,[[Scope]]就是其中的一個(gè)屬性,它指明了哪些東西可以被函數(shù)訪問(wèn)。
考慮下面的函數(shù)

function add(a,b){
    var sum = a + b;
    return sum;
}

當(dāng)函數(shù)add創(chuàng)建時(shí)候,add的[[Scope]]屬性會(huì)指向作用域鏈對(duì)象,該對(duì)象的初始位置指向全局對(duì)象,如下圖所示。

var t = add(1,2);

上述語(yǔ)句執(zhí)行了add函數(shù),對(duì)于函數(shù)的每一次執(zhí)行,瀏覽器會(huì)創(chuàng)建一個(gè)執(zhí)行環(huán)境的內(nèi)部對(duì)象,一個(gè)執(zhí)行環(huán)境定義了一個(gè)函數(shù)執(zhí)行時(shí)的環(huán)境。函數(shù)的每次執(zhí)行時(shí)對(duì)應(yīng)的執(zhí)行環(huán)境都說(shuō)唯一的。每一個(gè)執(zhí)行環(huán)境都有自己的作用域鏈,此對(duì)象的局部變量,thisarguments等組成活動(dòng)對(duì)象,插入在作用域鏈對(duì)象的最前端,也就是圖中所示的0號(hào)位置,當(dāng)運(yùn)行結(jié)束后,執(zhí)行環(huán)境和活動(dòng)對(duì)象都將銷(xiāo)毀。
函數(shù)的執(zhí)行過(guò)程中,每遇到一個(gè)變量,都會(huì)從作用域鏈的頂部,也就是0號(hào)位置查找該變量,如果查找成功則返回,查找失敗則按照作用域鏈查找下一個(gè)位置的對(duì)象,該例子中也就是1號(hào)位置的全局對(duì)象。

作用域鏈帶來(lái)的性能問(wèn)題

如上面所討論的那樣,每一次遇到讀取變量的時(shí)候,都意味著一次搜索作用域鏈的過(guò)程,如果搜索的作用域鏈的層次越多的話,將嚴(yán)重影響性能。
所以,當(dāng)在函數(shù)中使用全局變量的時(shí)候,所產(chǎn)生的代價(jià)是最大的,因?yàn)槿謱?duì)象一直處于作用域鏈的最末位置,讀取局部變量是最快的。
所以,一個(gè)提高效率的規(guī)則是盡可能的使用局部變量。如下面的代碼所示。

function demo(){
    var d = document,
        bd = d.body,
        div = d.getElementsByTagName("div");
    d.getElementById("id1").innerHTML = "aaa";
    //(許多使用document,body和div的操作)
}

上面的代碼首先將全局的document對(duì)象保存在了局部變量d中,這樣當(dāng)下次頻繁的使用document對(duì)象時(shí),僅僅需要從局部變量中即可獲得。

動(dòng)態(tài)作用域

js中實(shí)用的是靜態(tài)作用域,作用域鏈一般不可改變,但是withtry-catch可以改變作用域鏈,發(fā)生在函數(shù)的執(zhí)行時(shí)候

with語(yǔ)句
function withTest(){
    var foo = "sf";
    var obj = {foo:"abc"};
    with(obj){
        function f(){
            alert(foo);
        }
        (function(){
            alert(foo);
        })();
        f();
    }
}
withTest();

在函數(shù)聲明的時(shí)候,作用域鏈沒(méi)有考慮with的情況,當(dāng)函數(shù)執(zhí)行的時(shí)候,動(dòng)態(tài)生成with的對(duì)象,推入在作用域鏈的首位,這就意味著函數(shù)的局部變量存在作用域鏈的第二個(gè)位置,訪問(wèn)的代價(jià)提高了,雖然訪問(wèn)with對(duì)象的代價(jià)降低了,完全可以將with對(duì)象保存在局部變量中,故with語(yǔ)句不推薦使用。

try-catch語(yǔ)句
try{
    anErrorFunction();
}catch(e){
    errorHandler(e);
}

由于catch語(yǔ)句中只有一條語(yǔ)句,將error傳遞給errorHandler函數(shù),所以運(yùn)行時(shí)作用域鏈的改變不會(huì)影響性能。

什么是閉包

閉包是允許函數(shù)訪問(wèn)局部作用域之外的數(shù)據(jù)。即使外部函數(shù)已經(jīng)退出,外部函數(shù)的變量仍可以被內(nèi)部函數(shù)訪問(wèn)到。
因此閉包的實(shí)現(xiàn)需要三個(gè)條件:

內(nèi)部函數(shù)實(shí)用了外部函數(shù)的變量

外部函數(shù)已經(jīng)退出

內(nèi)部函數(shù)可以訪問(wèn)

function a(){
    var x = 0;
    return function(y){
        x = x + y;
        return x;
    }
}
var b = a();
b(1);

上述代碼在執(zhí)行的時(shí)候,b得到的是閉包對(duì)象的引用,雖然a執(zhí)行完畢后,但是a的活動(dòng)對(duì)象由于閉包的存在并沒(méi)有被銷(xiāo)毀,在執(zhí)行b(1)的時(shí)候,仍然訪問(wèn)到了x變量,并將其加1,若在此執(zhí)行b(1),則x是2,因?yàn)殚]包的引用b并沒(méi)有消除。

一個(gè)經(jīng)典的閉包的實(shí)例
//ul下面有3個(gè)li,實(shí)現(xiàn)點(diǎn)擊每個(gè)li,彈出li的序號(hào)
for(var i = 0,len = lis.length;i < len; i++){
    lis[i].onclick = function(i){
        return function(){
            alert(i);
        }
    }(i);
}

在這里,沒(méi)有把閉包直接給onclick事件,而是先定義了一個(gè)自執(zhí)行函數(shù),該函數(shù)中包含著閉包的函數(shù),i的值被保存在自執(zhí)行的函數(shù)中,當(dāng)閉包函數(shù)執(zhí)行后,會(huì)從自執(zhí)行函數(shù)中查找i,達(dá)到“保存”變量的目的。

注:匿名函數(shù)中的this指向的是window,故在匿名閉包函數(shù)使用父函數(shù)的this指針時(shí),需要將其存儲(chǔ)下來(lái),如 var that = this;

閉包的作用

模塊化代碼

私有成員

避免全局變量的污染

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

使用閉包所造成的性能問(wèn)題

如上面的描述,當(dāng)執(zhí)行閉包函數(shù)后,父函數(shù)所保留下來(lái)的活動(dòng)對(duì)象并不是在閉包函數(shù)的作用域鏈的首位(首位存放的是閉包的活動(dòng)對(duì)象),當(dāng)頻繁的訪問(wèn)跨作用域的標(biāo)識(shí)符時(shí)候,每次都會(huì)造成性能的損失,我們?nèi)匀豢梢詫⒊S玫目缱饔糜蜃兞看鎯?chǔ)在局部變量中,直接訪問(wèn)該局部變量

實(shí)用閉包所造成的內(nèi)存泄露問(wèn)題(IE9以下)

IE9及以下的版本使用的是引用計(jì)數(shù)的內(nèi)存回收機(jī)制,當(dāng)引用計(jì)數(shù)為0的時(shí)候?qū)?huì)回收,但有一種循環(huán)引用的情況

window.onload = function(){
    var el = document.getElementById("id");
    el.onclick = function(){
        alert(el.id);
    }
}

這段代碼執(zhí)行時(shí),將匿名函數(shù)對(duì)象賦值給elonclick屬性;然后匿名函數(shù)內(nèi)部又引用了el對(duì)象,存在循環(huán)引用,所以不能被回收;
(javascript 高級(jí)程序設(shè)計(jì)(第三版))
解決方法:

window.onload = function(){
    var el = document.getElementById("id");
    var id = el.id; //解除了循環(huán)引用
    el.onclick = function(){
        alert(id); //并沒(méi)有出現(xiàn)循環(huán)引用
    }
    el = null; // 將閉包引用的外部活動(dòng)對(duì)象清除
}

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

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

相關(guān)文章

  • 【進(jìn)階2-1期】深入淺出圖解作用鏈和閉包

    摘要:本期推薦文章從作用域鏈談閉包,由于微信不能訪問(wèn)外鏈,點(diǎn)擊閱讀原文就可以啦。推薦理由這是一篇譯文,深入淺出圖解作用域鏈,一步步深入介紹閉包。作用域鏈的頂端是全局對(duì)象,在全局環(huán)境中定義的變量就會(huì)綁定到全局對(duì)象中。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周開(kāi)始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第6天。 本...

    levius 評(píng)論0 收藏0
  • JS基礎(chǔ)知識(shí):變量對(duì)象、作用鏈和閉包

    摘要:前言這段時(shí)間一直在消化作用域鏈和閉包的相關(guān)知識(shí)。而作用域鏈則是這套規(guī)則這套規(guī)則的具體運(yùn)行。是變量對(duì)象的縮寫(xiě)那這樣放有什么好處呢我們知道作用域鏈保證了當(dāng)前執(zhí)行環(huán)境對(duì)符合訪問(wèn)權(quán)限的變量和函數(shù)的有序訪問(wèn)。 前言:這段時(shí)間一直在消化作用域鏈和閉包的相關(guān)知識(shí)。之前看《JS高程》和一些技術(shù)博客,對(duì)于這些概念的論述多多少少不太清楚或者不太完整,包括一些大神的技術(shù)文章。這也給我的學(xué)習(xí)上造成了一些困惑,...

    Keven 評(píng)論0 收藏0
  • js知識(shí)梳理6:關(guān)于函數(shù)的要點(diǎn)梳理(2)(作用鏈和閉包)

    摘要:在此例中,在匿名函數(shù)被返回后,它的作用域鏈初始化為包含函數(shù)的活動(dòng)對(duì)象和全局變量對(duì)象。函數(shù)在執(zhí)行完畢后,其活動(dòng)對(duì)象也不會(huì)被銷(xiāo)毀,因?yàn)槟涿瘮?shù)的作用域鏈仍然在引用這個(gè)活動(dòng)對(duì)象,結(jié)果就是只是的執(zhí)行環(huán)境的作用域鏈會(huì)被銷(xiāo)毀,其活動(dòng)對(duì)象會(huì)留在內(nèi)存中。 寫(xiě)在前面 注:這個(gè)系列是本人對(duì)js知識(shí)的一些梳理,其中不少內(nèi)容來(lái)自書(shū)籍:Javascript高級(jí)程序設(shè)計(jì)第三版和JavaScript權(quán)威指南第六版,...

    aristark 評(píng)論0 收藏0
  • javascript系列--javascript深入淺出圖解作用鏈和閉包

    摘要:變量對(duì)象也是有父作用域的。作用域鏈的頂端是全局對(duì)象。當(dāng)函數(shù)被調(diào)用的時(shí)候,作用域鏈就會(huì)包含多個(gè)作用域?qū)ο蟆.?dāng)函數(shù)要訪問(wèn)時(shí),沒(méi)有找到,于是沿著作用域鏈向上查找,在的作用域找到了對(duì)應(yīng)的標(biāo)示符,就會(huì)修改的值。 一、概要 對(duì)于閉包的定義(紅寶書(shū)P178):閉包就是指有權(quán)訪問(wèn)另外一個(gè)函數(shù)的作用域中的變量的函數(shù)。 關(guān)鍵點(diǎn): 1、閉包是一個(gè)函數(shù) 2、能夠訪問(wèn)另外一個(gè)函數(shù)作用域中的變量 二、閉包特性 對(duì)...

    Jensen 評(píng)論0 收藏0
  • JS 作用域鏈

    摘要:首先,在創(chuàng)建函數(shù)時(shí),作用域鏈內(nèi)就會(huì)先填入對(duì)象,圖片只例舉了全部變量中的一部分。然后,解釋器進(jìn)入函數(shù)的執(zhí)行環(huán)境,同樣的,首先填入父級(jí)的作用域鏈,就是的,包括了對(duì)象活動(dòng)對(duì)象。之后再把的活動(dòng)對(duì)象填入到作用域鏈最頂部,這就是的作用域鏈了。 之前學(xué)習(xí)JS函數(shù)部分時(shí),提到了作用域這一節(jié),但是因?yàn)槭褂貌牧蠒?shū)不同,今天在讀博客的時(shí)候發(fā)現(xiàn)其實(shí)還有一個(gè)知識(shí)點(diǎn)即作用域鏈,所以來(lái)寫(xiě)一些個(gè)人理解和認(rèn)識(shí)加深記憶。...

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

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

0條評(píng)論

閱讀需要支付1元查看
<