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

資訊專欄INFORMATION COLUMN

JS的閉包與this詳解

fireflow / 2788人閱讀

摘要:刪除對(duì)匿名函數(shù)的引用,以便釋放內(nèi)存在匿名函數(shù)從中被返回后,它的作用域鏈被初始化為包含函數(shù)的活動(dòng)對(duì)象和全局變量對(duì)象。閉包與變量我們要注意到,閉包只能取到任意變量的最后值,也就是我們保存的是活動(dòng)對(duì)象,而不是確定值。

工作中會(huì)遇到很多 this對(duì)象 指向不明的問題,你可能不止一次用過 _self = this 的寫法來傳遞this對(duì)象,它每每會(huì)讓我們覺得困惑和抓狂,我們很可能會(huì)好奇其中到底發(fā)生了什么。

一個(gè)問題

現(xiàn)在先來看一個(gè)具體的問題:

var name = "The Window";
var obj = {
    name: "My obj",
    getName: function() {
        return this.name;
    }
};

// 猜測(cè)下面的輸出和背后的邏輯(非嚴(yán)格模式下)
object.getName();
(object.getName)();
(object.getName = object.getName)();

如果上面的三個(gè)你都能答對(duì)并知道都發(fā)生了什么,那么你對(duì)JS的this了解的比我想象的要多,可以跳過這篇文章了,如果沒答對(duì)或者不明白,那么這篇文章會(huì)告訴你并幫你梳理下相關(guān)的知識(shí)。
它們的答案是:

object.getName();    // "My Obj"
(object.getName)();    // "My Obj"
(object.getName = object.getName)();    // "The Window"
函數(shù)的作用域

在函數(shù)被調(diào)用的時(shí)候,會(huì)創(chuàng)建一個(gè)執(zhí)行環(huán)境及相應(yīng)的作用域鏈,然后,使用arguments以及其他命名參數(shù)的值來初始化函數(shù)的活動(dòng)對(duì)象(activation object,簡(jiǎn)稱AO)。在作用域上,函數(shù)會(huì)逐層復(fù)制自身調(diào)用點(diǎn)的函數(shù)屬性,完成作用域鏈的構(gòu)建,直到全局執(zhí)行環(huán)境。

function compare(value1, value2) {
    return value1 - value2;
}

var result = compare(5, 10);

在這段代碼中,result通過var進(jìn)行了變量聲明提升,compare通過function函數(shù)聲明提升,在代碼執(zhí)行之前我們的全局變量對(duì)象中就會(huì)有這兩個(gè)屬性。

每個(gè)執(zhí)行環(huán)境都會(huì)有一個(gè)變量對(duì)象,包含存在的所有變量的對(duì)象。全局環(huán)境的變量對(duì)象始終存在,而像compare函數(shù)這樣的局部環(huán)境的變量對(duì)象,則只在函數(shù)執(zhí)行的過程中存在。當(dāng)創(chuàng)建compare()函數(shù)時(shí),會(huì)創(chuàng)建一個(gè)預(yù)先包含全局變量對(duì)象的作用域鏈,這個(gè)作用域鏈保存在內(nèi)部的[[Scope]]屬性中。

在調(diào)用compare函數(shù)時(shí),會(huì)為它創(chuàng)建一個(gè)執(zhí)行環(huán)境,然后復(fù)制函數(shù)的[[scope]]屬性中的對(duì)象構(gòu)建起執(zhí)行環(huán)境的作用域鏈。此后,又有一個(gè)活動(dòng)對(duì)象(變量對(duì)象)被創(chuàng)建并被推入執(zhí)行環(huán)境作用域鏈的前端。此時(shí)作用域鏈包含兩個(gè)變量對(duì)象:本地活動(dòng)對(duì)象和全局變量對(duì)象。顯然,作用域鏈本質(zhì)上是一個(gè)指向變量對(duì)象的指針列表,它只引用但不包含實(shí)際的變量對(duì)象。

當(dāng)訪問函數(shù)的變量時(shí),就會(huì)從作用域鏈中搜索。當(dāng)函數(shù)執(zhí)行完畢后,局部活動(dòng)對(duì)象就會(huì)被銷毀,內(nèi)存中僅保存全局作用域。

閉包

但是,閉包的情況有所不同,在一個(gè)函數(shù)內(nèi)部定義的函數(shù)會(huì)將外部函數(shù)的活動(dòng)對(duì)象添加到它的作用域鏈中去。

function create(property) {
    return function(object1, object2) {
        console.log(object1[property], object2[property]);
    };
}

var compare = create("name");
var result = compare({name: "Nicholas"}, {name: "Greg"}); // Nicholas Greg

// 刪除對(duì)匿名函數(shù)的引用,以便釋放內(nèi)存
compare = null;

在匿名函數(shù)從create()中被返回后,它的作用域鏈被初始化為包含create()函數(shù)的活動(dòng)對(duì)象和全局變量對(duì)象。這樣,該匿名函數(shù)就可以訪問create中定義的所有遍歷,更為重要的是當(dāng)create()函數(shù)執(zhí)行完畢后,其作用域鏈被銷毀,但是活動(dòng)對(duì)象不會(huì)銷毀,因?yàn)橐廊槐荒涿瘮?shù)引用。當(dāng)匿名函數(shù)別compare()被銷毀后,create()的活動(dòng)對(duì)象才會(huì)被銷毀。

閉包與變量

我們要注意到,閉包只能取到任意變量的最后值,也就是我們保存的是活動(dòng)對(duì)象,而不是確定值。

function create() {
    var result = [];
    for (var i = 0; i < 10; i++) {
        result[i] = function() {
            return i;
        };
    }
    return result;
}

create()[3](); // 10

我們通過閉包,讓每一個(gè)result的元素都能夠返回i的值,但是閉包包含的是同一個(gè)活動(dòng)對(duì)象i,而不是固定的1-10的值,所以返回的都是10。但我們可以通過值傳遞的方式創(chuàng)建另外一個(gè)匿名函數(shù)來滿足我們的需求。

function create() {
    var result = [];
    for (var i = 0; i < 10; i++) {
        // 通過值傳遞的方式固定i值
        result[i] = function(num) {
            // 這里閉包固定后的i值,即num值,來滿足我們的需求
            return function() {
                return num;
            };
        }(i);
    }
    return result;
}

create()[3](); // 3
閉包與this

我們知道this對(duì)象是基于函數(shù)的執(zhí)行環(huán)境綁定的,在全局的時(shí)候,this等于window,而當(dāng)函數(shù)作為某個(gè)對(duì)象的方法調(diào)用時(shí),this等于那個(gè)對(duì)象。不過,匿名函數(shù)的執(zhí)行環(huán)境具有全局性,因此this常常指向window。

var name = "The Window";
var obj = {
    name: "My obj",
    getName: function() {
        return function() {
            return this.name;
        };
    }
};

obj.getName()(); // "The Window"

前面說過,函數(shù)在被調(diào)用時(shí)會(huì)自動(dòng)取得兩個(gè)特殊變量: this和arguments,內(nèi)部函數(shù)在搜索這兩個(gè)變量時(shí),只會(huì)搜索到其活動(dòng)對(duì)象,所以永遠(yuǎn)不會(huì)訪問到外部函數(shù)的這兩個(gè)變量。如果我們想滿足需求,可以固定this對(duì)象并更名即可。

var name = "The Window";
var obj = {
    name: "My obj",
    getName: function() {
        // 固定this對(duì)象,形成閉包,防止跟特殊的this重名
        var that = this;
        return function() {
            return that.name;
        };
    }
};

obj.getName()(); // "My obj"
this的綁定

上面對(duì)this的說明可以說是非常的淺薄了,現(xiàn)在我們?cè)敿?xì)的整理下this關(guān)鍵字,它是函數(shù)作用域的特殊關(guān)鍵字,進(jìn)入函數(shù)執(zhí)行環(huán)境時(shí)會(huì)被自動(dòng)定義,實(shí)現(xiàn)原理相當(dāng)于自動(dòng)傳遞調(diào)用點(diǎn)的對(duì)象:

var obj = {
    name: "Nicholas",
    speak() {
        return this.name;
    },
    anotherSpeak(context) {
        console.log(context.name, context === this);
    }
};

obj.name;    //"Nicholas"
obj.speak();    // "Nicholas"
obj.anotherSpeak(obj);    // "Nicholas" true

可以看到,我們?cè)赼notherSpeak()中傳遞的context就是obj,也就是函數(shù)調(diào)用時(shí),執(zhí)行環(huán)境的this值。引擎的這種實(shí)現(xiàn)簡(jiǎn)化了我們的工作,自動(dòng)傳遞調(diào)用點(diǎn)的環(huán)境對(duì)象作為this對(duì)象。

我們要注意的是this只跟調(diào)用點(diǎn)有關(guān),而跟聲明點(diǎn)無關(guān)。這里你需要知道調(diào)用棧,也就是使我們到達(dá)當(dāng)前執(zhí)行位置而被調(diào)用的所有方法的棧,即所有嵌套的函數(shù)棧。

function baz() {
    // 調(diào)用棧是: `baz`
    // 我們的調(diào)用點(diǎn)是global scope(全局作用域)

    console.log( "baz" );
    bar(); // <-- `bar`的調(diào)用點(diǎn)
}

function bar() {
    // 調(diào)用棧是: `baz` -> `bar`
    // 我們的調(diào)用點(diǎn)位于`baz`

    console.log( "bar" );
    foo(); // <-- `foo`的調(diào)用點(diǎn)
}

function foo() {
    // 調(diào)用棧是: `baz` -> `bar` -> `foo`
    // 我們的調(diào)用點(diǎn)位于`bar`

    console.log( "foo" );
}

baz(); // <-- `baz`的調(diào)用點(diǎn)

我們整理了四種this對(duì)象綁定的規(guī)則:

默認(rèn)綁定
function foo() {
    console.log( this.a, this === window );
}
var a = 2;

window.a;    // 2
foo();    // 2 true

在這種規(guī)則下,函數(shù)調(diào)用為獨(dú)立的毫無修飾的函數(shù)引用調(diào)用的,此時(shí)foo的調(diào)用環(huán)境就是全局環(huán)境window,所以this就指向window,而在全局下聲明的所有對(duì)象都屬于window,導(dǎo)致結(jié)果為2。

但是在嚴(yán)格模式下,this不會(huì)被默認(rèn)綁定到全局對(duì)象。MDN文檔上寫到:

第一,在嚴(yán)格模式下通過this傳遞給一個(gè)函數(shù)的值不會(huì)被強(qiáng)制轉(zhuǎn)換為一個(gè)對(duì)象。對(duì)一個(gè)普通的函數(shù)來說,this總會(huì)是一個(gè)對(duì)象:不管調(diào)用時(shí)this它本來就是一個(gè)對(duì)象;還是用布爾值,字符串或者數(shù)字調(diào)用函數(shù)時(shí)函數(shù)里面被封裝成對(duì)象的this;還是使用undefined或者null調(diào)用函數(shù)式this代表的全局對(duì)象(使用call, apply或者bind方法來指定一個(gè)確定的this)。這種自動(dòng)轉(zhuǎn)化為對(duì)象的過程不僅是一種性能上的損耗,同時(shí)在瀏覽器中暴露出全局對(duì)象也會(huì)成為安全隱患,因?yàn)槿謱?duì)象提供了訪問那些所謂安全的JavaScript環(huán)境必須限制的功能的途徑。所以對(duì)于一個(gè)開啟嚴(yán)格模式的函數(shù),指定的this不再被封裝為對(duì)象,而且如果沒有指定this的話它值是undefined。

function foo() {
    "use strict";
    console.log( this );
}

foo();    // undefined

關(guān)于嚴(yán)格模式還需要注意的是,它的作用范圍只有當(dāng)前的函數(shù)或者

閱讀需要支付1元查看
<