免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
js - 基礎(chǔ) 之 預編譯總結(jié)

js運行步驟

  • 語法解析(檢查有無語法錯誤)

  • 預編譯

  • 解釋運行(將 js 翻譯成計算機識別的語言(0、1組成),翻譯一行執(zhí)行一行)

預編譯

【全局】:

  • 創(chuàng)建 GO( Grobal Object ) 對象

  • 找變量聲明

  • 找函數(shù)聲明

【函數(shù)】:

  • 創(chuàng)建 AO( Activation Object ) 對象(執(zhí)行上下文);

  • 找形參和變量聲明,將形參和變量名作為 AO 對象的屬性名,值為 undefined(有重復的名稱只寫一個即可);

  • 將形參與實參值統(tǒng)一(用實參的值替換 undefined);

  • 在函數(shù)體中找函數(shù)聲明,將函數(shù)名添加到 AO 對象的屬性中,值為函數(shù)體(如屬性名重復,則覆蓋前面的)。

【執(zhí)行上下文/執(zhí)行環(huán)境】組成:

  • 變量對象(Variable object,VO) :包含變量的對象,無法訪問。

  • 作用域鏈(Scope chain):作用域即變量對象,作用域鏈是一個由變量對象組成的帶頭結(jié)點的單向鏈表,其主要作用就是用來進行變量查找;而[[Scope]]屬性是一個指向這個鏈表頭節(jié)點的指針

  • this:指向一個環(huán)境對象

【參考】https://www.jianshu.com/p/76ed896bbf91

執(zhí)行環(huán)境(執(zhí)行上下文 execution context)

定義

有時也稱環(huán)境,執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù) ,決定了它們各自的行為。而每個執(zhí)行環(huán)境都有一個與之相關(guān)的變量對象,環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中。

執(zhí)行過程

當JavaScript解釋器初始化執(zhí)行代碼時,它首先默認進入全局執(zhí)行環(huán)境,從此刻開始,函數(shù)的每次調(diào)用都會創(chuàng)建一個新的執(zhí)行環(huán)境。
當javascript代碼被瀏覽器載入后,默認最先進入的是一個全局執(zhí)行環(huán)境。
當在全局執(zhí)行環(huán)境中調(diào)用執(zhí)行一個函數(shù)時,程序流就進入該被調(diào)用函數(shù)內(nèi),此時JS引擎就會為該函數(shù)創(chuàng)建一個新的執(zhí)行環(huán)境,并且將其壓入到執(zhí)行環(huán)境堆棧的頂部。瀏覽器總是執(zhí)行當前在堆棧頂部的執(zhí)行環(huán)境,一旦執(zhí)行完畢,該執(zhí)行環(huán)境就會從堆棧頂部被彈出,然后,進入其下的執(zhí)行環(huán)境執(zhí)行代碼。這樣,堆棧中的執(zhí)行環(huán)境就會被依次執(zhí)行并且彈出堆棧,直到回到全局執(zhí)行環(huán)境。

執(zhí)行環(huán)境完成可以分為創(chuàng)建執(zhí)行兩個階段。

1、在創(chuàng)建階段,解析器首先會創(chuàng)建一個變量對象【variable object】(函數(shù)中稱為活動對象【activation object】),它由定義在執(zhí)行環(huán)境中的變量、函數(shù)聲明、和參數(shù)組成。在這個階段,作用域鏈會被初始化,this的值也會被最終確定。
2、在執(zhí)行階段,代碼被解釋執(zhí)行。
具體過程:每次調(diào)用函數(shù),都會創(chuàng)建新的執(zhí)行上下文。在JavaScript解釋器內(nèi)部,每次調(diào)用執(zhí)行上下文,分為兩個階段:
2.1 創(chuàng)建階段【若是函數(shù),當函數(shù)被調(diào)用,但未執(zhí)行任何其內(nèi)部代碼之前】
在進入執(zhí)行上下文階段,只會將有 var,function修飾的變量或方法添加到變量對象中。

  • 創(chuàng)建作用域鏈(Scope Chain)

  • 創(chuàng)建變量對象(變量,函數(shù)和參數(shù))

  • 確定this的指向
    2.2 激活/代碼執(zhí)行階段:

  • 變量賦值

  • 函數(shù)引用,

  • 解釋/執(zhí)行其他代碼

變量對象 組成

  • 函數(shù)的所有形參 (如果是函數(shù)上下文)
    由名稱和對應值組成的一個變量對象的屬性被創(chuàng)建
    沒有實參,屬性值設(shè)為 undefined

  • 函數(shù)聲明
    由名稱和對應值(函數(shù)對象(function-object))組成一個變量對象的屬性被創(chuàng)建
    如果變量對象已經(jīng)存在相同名稱的屬性,則完全替換這個屬性

  • 變量聲明
    由名稱和對應值(undefined)組成一個變量對象的屬性被創(chuàng)建;
    如果變量名稱跟已經(jīng)聲明的形式參數(shù)或函數(shù)相同,則變量聲明不會干擾已經(jīng)存在的這類屬性

可以將每個執(zhí)行上下文抽象為一個對象并有三個屬性:executionContextObj = {  scopeChain: { /* 變量對象(variableObject)  所有父執(zhí)行上下文的變量對象*/ },   variableObject: { /*函數(shù) arguments/參數(shù),內(nèi)部變量和函數(shù)聲明 */ },   this: {} }

解釋器執(zhí)行代碼的偽邏輯(函數(shù))

1、查找調(diào)用函數(shù)的代碼。2、執(zhí)行函數(shù)代碼之前,先創(chuàng)建執(zhí)行上下文。3、進入創(chuàng)建階段:  3.1 初始化作用域鏈:  3.2 創(chuàng)建變量對象:   3.2.1 創(chuàng)建arguments對象,檢查上下文,初始化參數(shù)名稱和值并創(chuàng)建引用的復制。   3.2.2 掃描上下文的函數(shù)聲明:     為發(fā)現(xiàn)的每一個函數(shù),在變量對象上創(chuàng)建一個屬性——確切的說是函數(shù)的名字——其有一個指向函數(shù)在內(nèi)存中的引用。     如果函數(shù)的名字已經(jīng)存在,引用指針將被重寫。   3.2.3 掃描上下文的變量聲明:     為發(fā)現(xiàn)的每個變量聲明,在變量對象上創(chuàng)建一個屬性——就是變量的名字,并且將變量的值初始化為undefined     如果變量的名字已經(jīng)在變量對象里存在,將不會進行任何操作并繼續(xù)掃描。     3.3 求出上下文內(nèi)部“this”的值。4、激活/代碼執(zhí)行階段:在當前上下文上運行/解釋函數(shù)代碼,并隨著代碼一行行執(zhí)行指派變量的值。

demo:

function foo(i) {    var a = 'hello';    var b = function privateB() {    };    function c() {    }}foo(22);

1、創(chuàng)建階段:foo(22)函數(shù)調(diào)用時

fooExecutionContext = {    scopeChain: { ... },    variableObject: {        arguments: {            0: 22,            length: 1        },        i: 22,        c: pointer to function c()        a: undefined,        b: undefined    },    this: { ... }}

2、執(zhí)行階段:執(zhí)行流進入函數(shù)并且激活/代碼執(zhí)行階段

fooExecutionContext = {    scopeChain: { ... },    variableObject: {        arguments: {            0: 22,            length: 1        },        i: 22,        c: pointer to function c()        a: 'hello',        b: pointer to function privateB()    },    this: { ... }}

注意:

  1. 單線程

  2. 同步執(zhí)行

  3. 唯一的全局執(zhí)行環(huán)境

  4. 局部執(zhí)行環(huán)境的個數(shù)沒有限制

  5. 每次某個函數(shù)被調(diào)用,就會有個新的局部執(zhí)行環(huán)境為其創(chuàng)建,即使是多次調(diào)用的自身函數(shù)(即一個函數(shù)被調(diào)用多次,也會創(chuàng)建多個不同的局部執(zhí)行環(huán)境)。

執(zhí)行環(huán)境的分類

  1. Global Code,即全局的、不在任何函數(shù)里面的代碼,例如:一個js文件、嵌入在HTML頁面中的js代碼等。

  2. Function Code,即用戶自定義函數(shù)中的函數(shù)體JS代碼。

  3. Eval Code,即使用eval()函數(shù)動態(tài)執(zhí)行的JS代碼?!静煌扑],可忽略】

    全局環(huán)境:JavaScript代碼運行起來會首先進入該環(huán)境
    函數(shù)環(huán)境:當函數(shù)被調(diào)用執(zhí)行時,會進入當前函數(shù)中執(zhí)行代碼
    eval(不建議使用,可忽略)

解析:

  1. 全局執(zhí)行環(huán)境
    在瀏覽器中,其指window對象,是JS代碼開始運行時的默認環(huán)境。
    全局執(zhí)行環(huán)境的變量對象始終都是作用域鏈中的最后一個對象。

  2. 函數(shù)執(zhí)行環(huán)境
    當某個函數(shù)被調(diào)用時,會先創(chuàng)建一個執(zhí)行環(huán)境及相應的作用域鏈。然后使用arguments和其他命名參數(shù)的值來初始化執(zhí)行環(huán)境的變量對象。

執(zhí)行上下文(execution context)屬性:

  • 變量對象(variableObject)

  • 作用域鏈(scope chain)

  • this

【注】:
變量對象是與執(zhí)行上下文相關(guān)的數(shù)據(jù)作用域,存儲了在上下文中定義的變量和函數(shù)聲明。
全局上下文中的變量對象就是全局對象
在函數(shù)上下文中,我們用活動對象(activation object, AO)來表示變量對象。

活動對象和變量對象其實是一個東西,只是變量對象是規(guī)范上的或者說是引擎實現(xiàn)上的,不可在 JavaScript 環(huán)境中訪問,只有到當進入一個執(zhí)行上下文中,這個執(zhí)行上下文的變量對象才會被激活,所以才叫 activation object 吶,而只有被激活的變量對象,也就是活動對象上的各種屬性才能被訪問。

活動對象是在進入函數(shù)上下文時刻被創(chuàng)建的,它通過函數(shù)的 arguments 屬性初始化。arguments 屬性值是 Arguments 對象。

****** AO & VO *******
AO = VO function parameters arguments
AO 還包含函數(shù)的 parameters,以及 arguments 這個特殊對象

未進入執(zhí)行階段之前,變量對象(VO)中的屬性都不能訪問!但是進入執(zhí)行階段之后,變量對象(VO)被激活轉(zhuǎn)變?yōu)榱嘶顒訉ο?AO),里面的屬性都能被訪問了,然后開始進行執(zhí)行階段的操作。
它們其實都是同一個對象,只是處于執(zhí)行上下文的不同生命周期

函數(shù)參數(shù)的傳遞賦值問題 與 this的指向問題

函數(shù)參數(shù)的傳遞賦值問題

        function foo() {            console.log(this.a)        }        function active(fn) {            fn(); // 真實調(diào)用者,為獨立調(diào)用        }        var a = 20;        var obj = {            a: 10,            getA: foo        }        active(obj.getA); // 20        var name = "window";        var p = {          name: 'Perter',          getName: function() {            // 利用變量保存的方式保證其訪問的是p對象            var self = this;            return function() {              console.log(this.name) // window              return self.name;            }          }        }        var getName = p.getName();        var _name = getName();        console.log(_name); // Perter

this指向

var obj = { a: 1,  b: function(){  console.log(this); }}

1、作為對象調(diào)用時,指向該對象 obj.b(); // 指向obj
2、作為函數(shù)調(diào)用, var b = obj.b; b(); // 指向全局window
3、作為構(gòu)造函數(shù)調(diào)用 var b = new obj.b(); // this指向當前實例對象
4、作為call與apply調(diào)用 obj.b.apply(object, []); // this指向當前的object

作用域鏈創(chuàng)建

詞法作用域(lexical scoping)是指,函數(shù)在執(zhí)行時,使用的是它被定義時的作用域,而不是這個函數(shù)被調(diào)用時的作用域函數(shù)的作用域在函數(shù)定義的時候就決定了。這是因為函數(shù)有一個內(nèi)部屬性 [[scope]],當函數(shù)創(chuàng)建的時候,就會保存所有父變量對象到其中,你可以理解 [[scope]] 就是所有父變量對象的層級鏈,但是注意:[[scope]] 并不代表完整的作用域鏈!當函數(shù)激活時,進入函數(shù)上下文,創(chuàng)建 VO/AO 后,就會將活動對象添加到作用鏈的前端。至此,作用域鏈創(chuàng)建完畢。

demo:
```javascipt
function foo() {
function bar() {
...
}
}
// 函數(shù)創(chuàng)建時,各自的[[scope]]為:

    foo.[[scope]] = [      globalContext.VO    ];    bar.[[scope]] = [        fooContext.AO,        globalContext.VO    ];    ```

函數(shù)執(zhí)行上下文中作用域鏈和變量對象的創(chuàng)建過程總結(jié)

    ```    var scope = "global scope";    function checkscope(){        var scope2 = 'local scope';        return scope2;    }    checkscope();    ```    執(zhí)行過程如下:    1.checkscope 函數(shù)被創(chuàng)建,保存作用域鏈到 內(nèi)部屬性[[scope]]    ```    checkscope.[[scope]] = [        globalContext.VO    ];    ```    2.執(zhí)行 checkscope 函數(shù),創(chuàng)建 checkscope 函數(shù)執(zhí)行上下文,checkscope 函數(shù)執(zhí)行上下文被壓入執(zhí)行上下文棧    ```    ECStack = [        checkscopeContext,        globalContext    ];    ```    3.checkscope 函數(shù)并不立刻執(zhí)行,開始做準備工作,第一步:復制函數(shù)[[scope]]屬性創(chuàng)建作用域鏈    ```    checkscopeContext = {        Scope: checkscope.[[scope]],    }    ```    4.第二步:用 arguments 創(chuàng)建活動對象,隨后初始化活動對象,加入形參、函數(shù)聲明、變量聲明    ```    checkscopeContext = {        AO: {            arguments: {                length: 0            },            scope2: undefined        },        Scope: checkscope.[[scope]],    }    ```    5.第三步:將活動對象壓入 checkscope 作用域鏈頂端    ```    checkscopeContext = {        AO: {            arguments: {                length: 0            },            scope2: undefined        },        Scope: [AO, [[Scope]]]    }    ```    6.準備工作做完,開始執(zhí)行函數(shù),隨著函數(shù)的執(zhí)行,修改 AO 的屬性值    ```    checkscopeContext = {        AO: {            arguments: {                length: 0            },            scope2: 'local scope'        },        Scope: [AO, [[Scope]]]    }    ```    7.查找到 scope2 的值,返回后函數(shù)執(zhí)行完畢,函數(shù)上下文從執(zhí)行上下文棧中彈出    ```    ECStack = [        globalContext    ];    ```
### 閉包* 即使創(chuàng)建它的上下文已經(jīng)銷毀,它仍然存在(比如,內(nèi)部函數(shù)從父函數(shù)中返回)* 在代碼中引用了自由變量內(nèi)部函數(shù)引用了外部函數(shù)的變量,在外部函數(shù)上下文被銷毀后,其中的變量仍然可以被其內(nèi)部函數(shù)引用 因為:

fContext = {
Scope: [AO, checkscopeContext.AO, globalContext.VO],
}
```
對的,就是因為這個作用域鏈,f 函數(shù)依然可以讀取到 checkscopeContext.AO 的值,說明當 f 函數(shù)引用了 checkscopeContext.AO 中的值的時候,即使 checkscopeContext 被銷毀了,但是 JavaScript 依然會讓 checkscopeContext.AO 活在內(nèi)存中,f 函數(shù)依然可以通過 f 函數(shù)的作用域鏈找到它,正是因為 JavaScript 做到了這一點,從而實現(xiàn)了閉包這個概念。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
JavaScript作用域鏈其二:函數(shù)的生命周期
深入理解JavaScript系列(14):作用域鏈(Scope Chain)
手把手教會你JavaScript引擎如何執(zhí)行JavaScript代碼
JavaScript 預編譯與作用域
深入理解JavaScript作用域和作用域鏈
理解 JavaScript 作用域
更多類似文章 >>
生活服務(wù)
分享 收藏 導長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服