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

資訊專欄INFORMATION COLUMN

【JS腳丫系列】重溫閉包

MartinDai / 1467人閱讀

摘要:內(nèi)部的稱為內(nèi)部函數(shù)或閉包函數(shù)。過度使用閉包會導(dǎo)致性能下降。,閉包函數(shù)分為定義時,和運行時。循環(huán)會先運行完畢,此時,閉包函數(shù)并沒有運行。閉包只能取得外部函數(shù)中的最后一個值。事件綁定種的匿名函數(shù)也是閉包函數(shù)。而對象中的閉包函數(shù),指向。

閉包概念解釋:

閉包(也叫詞法閉包或者函數(shù)閉包)。

在一個函數(shù)parent內(nèi)聲明另一個函數(shù)child,形成了嵌套。函數(shù)child使用了函數(shù)parent的參數(shù)或變量,那么就形成了閉包。

閉包(closure)是可以訪問外部函數(shù)作用域中的變量或參數(shù)的函數(shù)。

此時,包裹的函數(shù)稱為外部函數(shù)。內(nèi)部的稱為內(nèi)部函數(shù)或閉包函數(shù)。(zyx456自定義:或稱為父函數(shù)和子函數(shù))。

閉包wiki

JS采用詞法作用域(lexical scoping),函數(shù)的執(zhí)行依賴于函數(shù)作用域,這個作用域是在函數(shù)定義時決定的,而不是函數(shù)調(diào)用時決定的。

詞法作用域:詞法作用域也叫靜態(tài)作用域,是指作用域在詞法解析階段就已經(jīng)確定了,不會改變。
動態(tài)作用域:是指作用域在運行時才能確定。

參看下面的例子,引自楊志的回答

var foo=1;

function static(){
    alert(foo);
}

!function(){
    var foo=2;
    
    static();
}();

在js中,會彈出1而非2,因為static的scope在創(chuàng)建時,記錄的foo是1。
如果js是動態(tài)作用域,那么他應(yīng)該彈出2

zyx456:識別閉包,在詞法分析階段已經(jīng)確定了。

當外部函數(shù)運行的時候,一個閉包就形成了,他由內(nèi)部函數(shù)的代碼以及任何內(nèi)部函數(shù)中指向外部函數(shù)局部變量的引用組成。

注意事項

01,閉包函數(shù)作用域中,使用的外部函數(shù)變量不會被立刻銷毀回收,所以會占用更多的內(nèi)存。過度使用閉包會導(dǎo)致性能下降。建議在非常有必要的時候才使用閉包。

02,同一個閉包函數(shù),所訪問的外部函數(shù)的變量是同一個變量。

03,如果把閉包函數(shù),賦值給不同的變量,那么不同的變量指向的是不同的閉包函數(shù),所使用的外部函數(shù)變量是不同的。

04,閉包函數(shù)分為定義時,和運行時。只有運行時,才會訪問外部函數(shù)的變量。

05,在for循環(huán)的閉包函數(shù),只有在運行時,才在作用域中尋找變量。for循環(huán)會先運行完畢,此時,閉包函數(shù)并沒有運行。

06,如果在for循環(huán)中,使用閉包的自執(zhí)行函數(shù)。那么閉包會使用for循環(huán)的變量i(0-*,假設(shè)i從0開始)。

07,一個函數(shù)里,可以有多個閉包。

匿名自執(zhí)行函數(shù),可以封裝私有變量。不會污染全局作用域。匿名函數(shù)中定義的任何變量,都會在執(zhí)行結(jié)束時被銷毀。

eval+with(僅了解)

在評論中賀師俊還提到,eval 和 with可以產(chǎn)生動態(tài)作用域的效果:

比如 with(o) { console.log(x) } 這個x實際有可能是 o.x 。所以這就不是靜態(tài)(詞法)作用域了。

var x = 0;
void function (code) {
    eval(code);
    console.log(x)
}("var x=1")

不過注意eval在strict模式下被限制了,不再能形成動態(tài)作用域了。

為什么閉包函數(shù)可以訪問外部函數(shù)的變量?

因為閉包函數(shù)的作用域鏈包含了外部函數(shù)的作用域。

如何創(chuàng)建閉包?

在一個函數(shù)類內(nèi)創(chuàng)建另外一個函數(shù)。內(nèi)部函數(shù)使用了外部函數(shù)的變量,就形成了閉包。

普通函數(shù)的內(nèi)部函數(shù)是閉包函數(shù)么?

zyx456:不是。

函數(shù)第一次被調(diào)用時,會發(fā)生什么?

當函數(shù)第一次被調(diào)用時,會創(chuàng)建一個執(zhí)行環(huán)境(execution context)和相應(yīng)的作用域鏈,并把作用域鏈賦值給一個內(nèi)部屬性(即[[Scope]])。

然后,使用this、arguments和其他參數(shù)來初始化函數(shù)的活動對象(activation object)。

在作用域鏈中,內(nèi)部函數(shù)的活動對象處于第一位,外部函數(shù)的活動對象始終處于第二位,外部函數(shù)的外部函數(shù)的活動對象處于第三位,……直至作為作用域鏈終點的全局執(zhí)行環(huán)境。

在函數(shù)執(zhí)行過程中,讀取和寫入變量的值,都需要在作用域鏈中查找變量。

每次調(diào)用JS函數(shù),會為之創(chuàng)建一個新的對象來保存所有的局部變量(函數(shù)定義的變量,函數(shù)參數(shù)。),把這個對象添加到作用域鏈中。函數(shù)體內(nèi)部的變量都保存在函數(shù)作用域內(nèi)。

我們將作用域鏈看做一個對象列表,而不是一個棧。

(zyx456:棧是一種線性表,僅允許在表的一端進行插入和刪除操作。)

當函數(shù)返回的時候,就從作用域鏈中將這個綁定變量的對象刪除。

如果這個函數(shù)不存在嵌套的函數(shù),也沒有其他引用指向這個綁定變量的對象,它就會被當做垃圾回收掉。

(zyx456:這個操作由瀏覽器自動完成)。

如果這個函數(shù)有嵌套的函數(shù),每個嵌套的函數(shù)都各自對應(yīng)一個作用域鏈。

這時:

內(nèi)部函數(shù),被作為返回值返回,或存儲在某處屬性中,就是會有一個外部引用指向這個它,那么它就不會被當做垃圾回收,并且它所使用外部變量所在的對象也不會被當做垃圾回收。只有內(nèi)部函數(shù)被銷毀后,外部函數(shù)的活動對象才會被銷毀。

在函數(shù)中訪問一個變量時,就會從作用域鏈中查找變量。

一般來講,當函數(shù)執(zhí)行完畢后,局部活動對象就會被銷毀,內(nèi)存中僅保存全局作用域(全局執(zhí)行環(huán)境的環(huán)境對象)。

但是,閉包的情況又有所不同。閉包函數(shù)的作用域鏈上有外部函數(shù)的作用域鏈。所以閉包函數(shù)可以訪問外部函數(shù)的變量。

閉包函數(shù)必須返回(return)么,return這個閉包函數(shù)?

zyx456:不必要返回,只要使用外部函數(shù)的變量即可。

代碼:

function fn1() {
    var a = 1;
    function fn2() {
        console.log(a);
    }
    fn2();
}
fn1();
如果用不同的變量引用函數(shù)中的閉包函數(shù),那么是不同的閉包變量。

簡單的例子:

function outter(){
    var x = 0;
    return function(){
        return x++;
    }
}
var a = outter();
console.log(a());
console.log(a());
var b = outter();
console.log(b());
console.log(b());

運行結(jié)果為:
0
1
0
1

閉包的用途:

可以創(chuàng)建私有變量。

因為只有閉包函數(shù)可以訪問外部函數(shù)的變量。

因為在閉包內(nèi)部保持了對外部活動對象的訪問,但外部的變量卻無法直接訪問內(nèi)部,避免了全局污染;

function setMoyu(){
    var name = "moyu";
    return function(newValue){
        name=newValue;
        console.log(name);
        
    }
}

var setValue = setMoyu();
setValue("world");//world
/*zyx456:這時name是私有屬性了,只能通過閉包函數(shù)設(shè)置它*/
閉包的缺點?

可能導(dǎo)致內(nèi)存占用過多,因為閉包攜帶了自身的函數(shù)作用域。

閉包只能取得外部函數(shù)中的最后一個值。

作用域:

變量聲明如果不使用 var 關(guān)鍵字,那么它就是一個全局變量,即便它在函數(shù)內(nèi)定義。

變量生命周期

全局變量的作用域是全局性的,即在整個JS程序中,全局變量處處都在。

而在函數(shù)內(nèi)部聲明的變量,只在函數(shù)內(nèi)部起作用。

這些變量是局部變量,作用域是局部性的;

函數(shù)的參數(shù)也是局部性的,只在函數(shù)內(nèi)部起作用。

在JS中,所有函數(shù)都能訪問它們上一層的作用域。

例子:

function compare(value1, value2){
    if (value1 < value2){
        return -1;
    } else if (value1 > value2){
        return 1;
    } else {
        return 0;
    }
}

var result = compare(5, 10);
內(nèi)存泄漏

由于IE 的JS對象和DOM對象使用不同的垃圾收集方式,因此閉包在IE中會導(dǎo)致內(nèi)存泄漏的問題,也就是無法銷毀駐留在內(nèi)存中的元素。

事件綁定種的匿名函數(shù)也是閉包函數(shù)。

如果閉包的作用域鏈中保存著一個HTML元素,那么就意味著該元素將無法被銷毀。

function box() {
    var oDiv = document.getElementById("oDiv"); //oDiv 用完之后一直駐留在內(nèi)存
    oDiv.onclick = function () {
        alert(oDiv.innerHTML); //這里用oDiv 導(dǎo)致內(nèi)存泄漏
    };
}
box();

那么在最后應(yīng)該將oDiv 解除引用來避免內(nèi)存泄漏。

function box() {
    var oDiv = document.getElementById("oDiv");
    var text = oDiv.innerHTML;
    oDiv.onclick = function () {
        alert(text);
    };
    oDiv = null; //解除引用
}

PS:如果并沒有使用解除引用,那么需要等到瀏覽器關(guān)閉才得以釋放。

閉包和this和arguments

閉包函數(shù)中的this問題

對于某個函數(shù)來說,如果函數(shù)在全局環(huán)境中,this指向window。如果在對象中,就指向這個對象。

而對象中的閉包函數(shù),this指向window。因為閉包并不屬于這個對象的屬性或方法。

var user = "The Window";
var obj = {
    user : "The Object",
    getUserFunction : function () {
        return function () { //閉包不屬于obj,里面的this 指向window
            return this.user;
        };
    }
};
alert(obj.getUserFunction()()); //The window
//可以強制指向某個對象
alert(obj.getUserFunction().call(obj)); //The Object
//也可以從上一個作用域中得到對象
getUserFunction : function () {
    var that = this; //從對象的方法里得對象
    return function () {
        return that.user;
    };
}

例子:

var self = this; // 將this保存至一個變量中,以便嵌套的函數(shù)能夠訪問它

綁定arguments的問題與之類似。

arguments并不是一個關(guān)鍵字,但在調(diào)用每個函數(shù)時都會自動聲明它,由于閉包具有自己所綁定的arguments,因此閉包內(nèi)無法直接訪問外部函數(shù)的參數(shù)數(shù)組,除非外部函數(shù)將參數(shù)數(shù)組保存到另外一個變量中:

var outerArguments = arguments;  //保存起來以便嵌套的函數(shù)能使用它

在通過call()或apply()改變函數(shù)執(zhí)行環(huán)境的情況下,this就會指向其他對象。

例子:

var scope = "global scope";             // 全局變量
function checkscope() {
        var scope = "local scope";      // 局部變量
        function f() { return scope; }  // 在作用域中返回這個值
        return f();
}
checkscope()                            // => "local scope"

checkscope()函數(shù)聲明了一個局部變量,并定義了一個函數(shù)f(),函數(shù)f()返回了這個變量的值,最后將函數(shù)f()的執(zhí)行結(jié)果返回。

你應(yīng)當非常清楚為什么調(diào)用checkscope()會返回"local scope"?,F(xiàn)在我們對這段代碼做一點改動。

var scope = "global scope";             // 全局變量
function checkscope() {
        var scope = "local scope";      // 局部變量
        function f() { return scope; }  // 在作用域中返回這個值
        return f;
}
checkscope()()                          // 返回值是什么?//local scope

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

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

相關(guān)文章

  • JS腳丫系列重溫bind

    摘要:和構(gòu)造函數(shù)構(gòu)造函數(shù)可以使用,然后再次創(chuàng)建實例。提供的值被忽略,提供的那些參數(shù)仍然會被前置到構(gòu)造函數(shù)調(diào)用的前面。在這種情況下,指向全局作用域?,F(xiàn)在將作為的方法來調(diào)用,傳入這些實參用于構(gòu)造函數(shù)。 概念 bind() 方法會返回一個新函數(shù)(稱為綁定函數(shù)),綁定函數(shù)與原函數(shù)(使用bind()的函數(shù))具有相同的函數(shù)體,但是綁定函數(shù)有新的this值和參數(shù)。 說白了,bind()就是創(chuàng)建一個有著新t...

    MASAILA 評論0 收藏0
  • 重溫基礎(chǔ)】19.閉包

    摘要:系列目錄復(fù)習資料資料整理個人整理重溫基礎(chǔ)篇重溫基礎(chǔ)對象介紹重溫基礎(chǔ)對象介紹重溫基礎(chǔ)介紹重溫基礎(chǔ)相等性判斷本章節(jié)復(fù)習的是中的關(guān)于閉包,這個小哥哥呀,看看。這里隨著閉包函數(shù)的結(jié)束,執(zhí)行環(huán)境銷毀,變量回收。 本文是 重溫基礎(chǔ) 系列文章的第十九篇。今日感受:將混亂的事情找出之間的聯(lián)系,也是種能力。 系列目錄: 【復(fù)習資料】ES6/ES7/ES8/ES9資料整理(個人整理) 【重溫基礎(chǔ)】...

    nanfeiyan 評論0 收藏0
  • 重溫基礎(chǔ)】4.函數(shù)

    摘要:本文是重溫基礎(chǔ)系列文章的第四篇。系列目錄復(fù)習資料資料整理個人整理重溫基礎(chǔ)語法和數(shù)據(jù)類型重溫基礎(chǔ)流程控制和錯誤處理重溫基礎(chǔ)循環(huán)和迭代本章節(jié)復(fù)習的是中的基礎(chǔ)組件之一,函數(shù),用來復(fù)用特定執(zhí)行邏輯。箭頭函數(shù)不能使用命令,即不能用作函數(shù)。 本文是 重溫基礎(chǔ) 系列文章的第四篇。今日感受:常懷感恩之心,對人對己。 系列目錄: 【復(fù)習資料】ES6/ES7/ES8/ES9資料整理(個人整理) 【重溫基...

    maxmin 評論0 收藏0
  • 重溫基礎(chǔ)】22.內(nèi)存管理

    摘要:內(nèi)存泄露內(nèi)存泄露概念在計算機科學中,內(nèi)存泄漏指由于疏忽或錯誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存。判斷內(nèi)存泄漏,以字段為準。 本文是 重溫基礎(chǔ) 系列文章的第二十二篇。 今日感受:優(yōu)化學習方法。 系列目錄: 【復(fù)習資料】ES6/ES7/ES8/ES9資料整理(個人整理) 【重溫基礎(chǔ)】1-14篇 【重溫基礎(chǔ)】15.JS對象介紹 【重溫基礎(chǔ)】16.JSON對象介紹 【重溫基礎(chǔ)】1...

    Pandaaa 評論0 收藏0
  • 【ES6腳丫系列】箭頭函數(shù)

    摘要:箭頭函數(shù)本文字符數(shù),閱讀時間約分鐘左右。箭頭函數(shù)等于說,只保留了函數(shù)的參數(shù)和返回。箭頭函數(shù)體內(nèi)的,繼承的是外層代碼塊的。所以,不用用箭頭函數(shù)聲明對象的方法。不可以使用命令因此箭頭函數(shù)不能用作函數(shù)。 【01】ES6箭頭函數(shù) 本文字符數(shù)4300+,閱讀時間約8分鐘左右。 【01】箭頭函數(shù) 等于說,只保留了函數(shù)的參數(shù)和返回。省略function和return。 寫法: (形參) => {st...

    tinyq 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<