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

資訊專欄INFORMATION COLUMN

JavaScript中的執(zhí)行上下文和變量對象

why_rookie / 1287人閱讀

摘要:以上簡單總結(jié)了下對執(zhí)行上下文和變量對象的理解,主要在于記錄總結(jié)一下學(xué)習(xí)成果,目前文章的水平實在不敢談分享。

執(zhí)行上下文(Execution Context)

文章同步到github javaScript中的執(zhí)行上下文和變量對象

JavaScript代碼執(zhí)行的過程,包括編譯和執(zhí)行兩個階段,編譯就是通過詞法分析,構(gòu)建抽象抽象語法樹,并編譯成機器識別的指令,在JavaScript代碼編譯階段,作用域規(guī)則就已經(jīng)確定了;在代碼執(zhí)行階段,或者函數(shù)一旦調(diào)用,便會創(chuàng)建執(zhí)行上下文(Execution Context),也叫執(zhí)行環(huán)境

在ECMA-262中有如下一段定義

當(dāng)控制器轉(zhuǎn)入 ECMA 腳本的可執(zhí)行代碼時,控制器會進入一個執(zhí)行環(huán)境。當(dāng)前活動的多個執(zhí)行環(huán)境在邏輯上形成一個棧結(jié)構(gòu)。該邏輯棧的最頂層的執(zhí)行環(huán)境稱為當(dāng)前運行的執(zhí)行環(huán)境。任何時候,當(dāng)控制器從當(dāng)前運行的執(zhí)行環(huán)境相關(guān)的可執(zhí)行代碼轉(zhuǎn)入與該執(zhí)行環(huán)境無關(guān)的可執(zhí)行代碼時,會創(chuàng)建一個新的執(zhí)行環(huán)境。新建的這個執(zhí)行環(huán)境會推入棧中,成為當(dāng)前運行的執(zhí)行環(huán)境.

這也是一個抽象的概念,在一段JavaScript代碼中,會創(chuàng)建多個執(zhí)行上下文,執(zhí)行上下文定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù), ,通過閱讀規(guī)范及相關(guān)文檔,了解到執(zhí)行上下文(簡稱EC)主要包括三個點,用偽代碼表示如下:

EC = {
    this: // 綁定this指向為當(dāng)前執(zhí)行上下文, 如果函數(shù)屬于全局函數(shù),則this指向window
    scopeChain: [] // 創(chuàng)建當(dāng)前執(zhí)行環(huán)境的作用域鏈,
    VO: {} // 當(dāng)前環(huán)境的變量對象(Variable Object),每個環(huán)境都有一個與之關(guān)聯(lián)的變量對象
}

看下面這一段代碼:

var a = 1;
function foo() {
    var b = 2;
    function bar() {
        console.log(b)
    }
    bar()
    console.log(a);
}

foo()

1.執(zhí)行這段代碼,首先會創(chuàng)建全局上下文globleEC,并推入執(zhí)行上下文棧中;

2.當(dāng)調(diào)用foo()時便會創(chuàng)建foo的上下文fooEC,并推入執(zhí)行上下文棧中;

3.當(dāng)調(diào)用bar()時便會創(chuàng)建bar的上下文barEC,并推入執(zhí)行上下文棧中;

4.當(dāng)bar函數(shù)執(zhí)行完,barEC便會從執(zhí)行上下文棧中彈出;

5.當(dāng)foo函數(shù)執(zhí)行完,fooEC便會從執(zhí)行上下文棧中彈出;

6.在瀏覽器窗口關(guān)閉后,全局上下文globleEC便會從執(zhí)行上下文棧中彈出;

總結(jié): 棧底永遠都是全局上下文,而棧頂就是當(dāng)前正在執(zhí)行的上下文

再舉一個例子結(jié)合瀏覽器開發(fā)者工具來看看到底什么執(zhí)行上線文

function foo() {
    bar()
    console.log("foo")
}

function bar() {
    baz()
    console.log("bar")
}

function baz() {
    debugger  // 打斷點觀察執(zhí)行上下文棧中的情況
}

可以看到當(dāng)前baz正在執(zhí)行,所以棧頂是baz的執(zhí)行上下文,而棧底永遠都是Global上下文

繼續(xù)執(zhí)行,baz函數(shù)執(zhí)行完成后,從執(zhí)行上下文棧頂彈出,繼續(xù)執(zhí)行bar函數(shù)內(nèi)后面的代碼,bar函數(shù)執(zhí)行完,bar的執(zhí)行上下文從棧中彈出;然后執(zhí)行foo函數(shù)后面的代碼,foo函數(shù)執(zhí)行完后,從執(zhí)行上下文從棧中彈出;最后全局上下文從執(zhí)行上下文從棧中彈出,清空執(zhí)行上下文從棧。

變量對象(Variable Object):
每一個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象,是一個抽象的概念,環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中。雖然我們編寫的代碼無法訪問這個對象,但解析器在處理數(shù)據(jù)時會在后臺使用它們。

當(dāng)瀏覽器第一次加載js腳本程序的時候, 默認進入全局執(zhí)行環(huán)境, 此次的全局環(huán)境變量對象為window, 在代碼中可以訪問。

如果環(huán)境是函數(shù), 則將此活動對象做為當(dāng)前上下文的變量對象(VO = AO), 此時變量對象是不可通過代碼來訪問的,下面主要對活動對象進行講解。

活動對象(Activation Object) 1.初始化活動對象(下文縮寫為AO)

當(dāng)函數(shù)一調(diào)用,立刻創(chuàng)建當(dāng)前上下文的活動對象, 并將活動對象作為變量對象,通過arguments屬性初始化,值為arguments對象(傳入的實參集合,與形參無關(guān),形參做為局部環(huán)境的局部變量被定義)

AO = {
  arguments: 
};

arguments對象有以下屬性:

length: 真正傳遞參數(shù)的個數(shù);

callee: 指向當(dāng)前函數(shù)的引用,也就是被調(diào)用的函數(shù);

"類index": 字符串類型的整數(shù), 值就是arguments對象中對象下標的值,arguments對象應(yīng)和數(shù)組加以區(qū)別, 它就是arguments對象,只是能和數(shù)組具有相同的length屬性,和可以通過下標來訪問值

function show (a, b, c) {
    // 通過Object.prototype.toString.call()精準判斷類型, 證明arguments不同于數(shù)組類型
    var arr = [1, 2, 3];
    console.log(Object.prototype.toString.call(arr)); // [object Array]

    console.log(Object.prototype.toString.call(arguments)); // [object Arguments]

    console.log(arguments.length) // 2  傳遞進來實參的個數(shù)

    console.log(arguments.callee === show) // true 就是被調(diào)用的函數(shù)show自身

    //參數(shù)共享

    console.log(a === arguments[0]) // true

    a = 15;

    console.log(arguments[0]) // 15

    arguments[0] = 25;

    console.log(a)  // 25;

    但是,對于沒有傳進來的參數(shù)c, 和arguments的第三個索引是不共享的

    c = 25;

    console.log(arguments[2]) // undefined

    argument[2] = 35;

    console.log(c) // 25

}

show(10, 20);

接著往下走,這才是關(guān)鍵的地方,執(zhí)行環(huán)境的代碼被分成兩個階段來處理:

進入執(zhí)行環(huán)境

執(zhí)行函數(shù)的代碼

2.進入執(zhí)行環(huán)境

函數(shù)如果被調(diào)用, 進入執(zhí)行環(huán)境(上下文),并立即創(chuàng)建活動對象, 通過arguments屬性初始化, 與此同時會掃描執(zhí)行環(huán)境中的所有形參、所有函數(shù)聲明、所有變量聲明, 添加到活動對象(AO)中, 并確定this的值,然后會開始執(zhí)行代碼。

在進入執(zhí)行環(huán)境這個階段:

所有形參聲明:

形參名稱作為活動對象屬性被創(chuàng)建, 如果傳遞實參, 值就為實參值, 如果沒有傳遞參數(shù), 值就為undefined

所有函數(shù)聲明:

函數(shù)名稱作為活動對象的屬性被創(chuàng)建,值是一個指針在內(nèi)存中, 指向這個函數(shù),如果變量對象已經(jīng)存在相同名稱的屬性, 則完全替換。

所有變量聲明:

所有變量名稱作為活動對象的屬性被創(chuàng)建, 值為undefined,但是和函數(shù)聲明不同的是, 如果變量名稱跟已經(jīng)存在的屬性(形式參數(shù)和函數(shù))相同、則不會覆蓋
function foo(a, b) {
    var c = 10;
    function d() {
        console.log("d");
    }
    var e = function () {
        console.log("e");
    };
    (function f() {})
    if (true) {
        var g = 20;
    } else {
        var h = 30;
    }
}

foo(10);

此時在進入foo函數(shù)執(zhí)行上下文時,foo的活動對象fooAO為:

fooAO = {
    arguments: {
        0: 10,
        length: 1
    },
    a: 10,
    b: undefined,
    c: undefined,
    d:   //指向d函數(shù)的指針,
    e: undefined,
    g: undefined,
    h: undefined  // 雖然else中的代碼永遠不會執(zhí)行,但是h仍然是活動對象中的屬性
}

這個例子做如下幾點說明:

1.關(guān)于函數(shù),只會創(chuàng)建函數(shù)聲明作為活動對象的屬性, 而f函數(shù)作為函數(shù)表達式并不會出現(xiàn)在活動對象(AO)中

2.e雖然值是一個函數(shù), 但是作為變量屬性被活動對象創(chuàng)建

3.代碼執(zhí)行階段

在進入執(zhí)行上下文階段,活動對象擁有了屬性,但是很多屬性值為undefined, 到代碼執(zhí)行階段就開始為這些屬性賦值了

還是上面的代碼例子, 此時活動對象如下:

fooAO = {
    arguments: {
        0: 10,
        length: 1
    },
    a: 10,
    b: undefined,
    c: 10, // 賦值為undefined
    d:   //指向d函數(shù)的指針,
    e:   // 指向e函數(shù)的指針
    g: 20,
    h: undefined  // 聲明h變量,但是沒有賦值
}

變量對象包括:{ arguments對象+函數(shù)形參+內(nèi)部變量+函數(shù)聲明(但不包含表達式) }

這時這個活動對象, 即作為當(dāng)前執(zhí)行環(huán)境的變量對象會被推到此執(zhí)行環(huán)境作用域鏈的最前端(作用域鏈本篇不做介紹,會在下一篇文章中多帶帶講解作用域和作用域鏈), 假定執(zhí)行環(huán)境為一個對象,則整個執(zhí)行環(huán)境可以訪問到的屬性如下:

偽代碼如下:

fooExecutionContext = {
    scopeChain: [], //fooAO +所有父執(zhí)行環(huán)境的活動對象,
    fooAO: {
        arguments: {
            0: 10,
            length: 1
        },
        a: 10,
        b: undefined,
        c: 10, // 賦值為undefined
        d:   //指向d函數(shù)的指針,
        e:   // 指向e函數(shù)的指針
        g: 20,
        h: undefined
    },
    this: 當(dāng)前執(zhí)行環(huán)境的上下文指針
}

補充:

下面的例子為了說明一下變量聲明的順序及變量同名不會影響函數(shù)聲明

console.log(foo); //  foo的函數(shù)體
var foo = 10;
console.log(foo) // 10
function foo() {};
foo = 20;
console.log(foo); // 20

在代碼執(zhí)行之前, 就會讀取函數(shù)聲明,變量聲明的順序在函數(shù)聲明和形參聲明之后, 整個流程如下:

1. 進入執(zhí)行環(huán)境階段:

1. var VO = {}
2. VO[foo] = "foo函數(shù)指針"
3. 掃描到var foo = 10,

 // 但是foo做為function已經(jīng)聲明,所以變量聲明不會影響同名的函數(shù)聲明,如果代碼中沒有foo函數(shù)聲明的話,則foo為undefined

代碼執(zhí)行階段:

1. VO[foo] = 10;
2. VO[foo] = 20;

解析代碼完成。

以上簡單總結(jié)了下對執(zhí)行上下文和變量對象的理解,主要在于記錄總結(jié)一下學(xué)習(xí)成果,目前文章的水平實在不敢談分享。如有理解不對的地方還請各位大神多多指教,想了解更深可以去查看本文最后主要參考資料的鏈接,都是經(jīng)典啊,相信看完也就理解了。

本文主要參考資料:

JavaScript高級程序設(shè)計(第3版)
ECMAScript5.1中文版--執(zhí)行環(huán)境
前端基礎(chǔ)進階(二):執(zhí)行上下文詳細圖解
前端基礎(chǔ)進階(三):變量對象詳解
深入理解JavaScript系列(11):執(zhí)行上下文(Execution Contexts)
深入理解JavaScript系列(12):變量對象(Variable Object)

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

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

相關(guān)文章

  • JavaScript-作用域-執(zhí)行下文-變量對象-作用域鏈

    摘要:變量對象作用域鏈因為變量對象在執(zhí)行上下文進入執(zhí)行階段時,就變成了活動對象,因此圖中使用了來表示。 作用域 作用域就是變量與函數(shù)的可訪問范圍,即作用域控制著變量與函數(shù)的可見性和生命周期。在 JavaScript 中,變量的作用域有全局作用域和局部作用域兩種。JavaScript 采用詞法作用域(lexical scoping),也就是靜態(tài)作用域。 靜態(tài)作用域 函數(shù)的作用域在函數(shù)定義的時候...

    liangzai_cool 評論0 收藏0
  • JavaScript-作用域-執(zhí)行下文-變量對象-作用域鏈

    摘要:變量對象作用域鏈因為變量對象在執(zhí)行上下文進入執(zhí)行階段時,就變成了活動對象,因此圖中使用了來表示。 作用域 作用域就是變量與函數(shù)的可訪問范圍,即作用域控制著變量與函數(shù)的可見性和生命周期。在 JavaScript 中,變量的作用域有全局作用域和局部作用域兩種。JavaScript 采用詞法作用域(lexical scoping),也就是靜態(tài)作用域。 靜態(tài)作用域 函數(shù)的作用域在函數(shù)定義的時候...

    MonoLog 評論0 收藏0
  • JavaScript深入之變量對象

    摘要:深入系列第四篇,具體講解執(zhí)行上下文中的變量對象與活動對象。下一篇文章深入之作用域鏈本文相關(guān)鏈接深入之執(zhí)行上下文棧深入系列深入系列目錄地址。 JavaScript深入系列第四篇,具體講解執(zhí)行上下文中的變量對象與活動對象。全局上下文下的變量對象是什么?函數(shù)上下文下的活動對象是如何分析和執(zhí)行的?還有兩個思考題幫你加深印象,快來看看吧! 前言 在上篇《JavaScript深入之執(zhí)行上下文?!分?..

    Zachary 評論0 收藏0
  • JAVASCRIPT FUNCTIONS

    摘要:在中,一個未使用明確標識符的函數(shù)被稱為一個匿名函數(shù)。記住在中,由關(guān)鍵字聲明的變量是一個局部變量,而忽略了這個關(guān)鍵字則會創(chuàng)建一個全局變量。函數(shù)被賦值給一個局部變量,在外部無法訪問它。這個函數(shù)表達式的變種被稱為一個命名的函數(shù)表達式。 本文是@堂主 對《Pro JavaScript with Mootools》一書的第二章函數(shù)部分知識點講解的翻譯。該書的作者 Mark Joseph Obce...

    Cympros 評論0 收藏0

發(fā)表評論

0條評論

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