小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

js - 基礎(chǔ) 之 預(yù)編譯總結(jié)

 印度阿三17 2019-03-22

js運(yùn)行步驟

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

  • 預(yù)編譯

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

預(yù)編譯

【全局】:

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

  • 找變量聲明

  • 找函數(shù)聲明

【函數(shù)】:

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

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

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

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

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

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

  • 作用域鏈(Scope chain):作用域即變量對(duì)象,作用域鏈?zhǔn)且粋€(gè)由變量對(duì)象組成的帶頭結(jié)點(diǎn)的單向鏈表,其主要作用就是用來進(jìn)行變量查找;而[[Scope]]屬性是一個(gè)指向這個(gè)鏈表頭節(jié)點(diǎn)的指針

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

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

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

定義

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

執(zhí)行過程

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

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

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

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

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

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

  • 變量賦值

  • 函數(shù)引用,

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

變量對(duì)象 組成

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

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

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

可以將每個(gè)執(zhí)行上下文抽象為一個(gè)對(duì)象并有三個(gè)屬性:

executionContextObj = {
  scopeChain: { /* 變量對(duì)象(variableObject)  所有父執(zhí)行上下文的變量對(duì)象*/ }, 
  variableObject: { /*函數(shù) arguments/參數(shù),內(nèi)部變量和函數(shù)聲明 */ }, 
  this: {} 
}

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

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

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

2、執(zhí)行階段:執(zhí)行流進(jìn)入函數(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)境的個(gè)數(shù)沒有限制

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

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

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

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

  3. Eval Code,即使用eval()函數(shù)動(dòng)態(tài)執(zhí)行的JS代碼。【不推薦,可忽略】

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

解析:

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

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

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

  • 變量對(duì)象(variableObject)

  • 作用域鏈(scope chain)

  • this

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

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

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

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

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

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

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

        function foo() {
            console.log(this.a)
        }

        function active(fn) {
            fn(); // 真實(shí)調(diào)用者,為獨(dú)立調(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對(duì)象
            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、作為對(duì)象調(diào)用時(shí),指向該對(duì)象 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指向當(dāng)前實(shí)例對(duì)象
4、作為call與apply調(diào)用 obj.b.apply(object, []); // this指向當(dāng)前的object

作用域鏈創(chuàng)建

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

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

    foo.[[scope]] = [
      globalContext.VO
    ];

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

函數(shù)執(zhí)行上下文中作用域鏈和變量對(duì)象的創(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í)行,開始做準(zhǔn)備工作,第一步:復(fù)制函數(shù)[[scope]]屬性創(chuàng)建作用域鏈
    ```
    checkscopeContext = {
        Scope: checkscope.[[scope]],
    }
    ```
    4.第二步:用 arguments 創(chuàng)建活動(dòng)對(duì)象,隨后初始化活動(dòng)對(duì)象,加入形參、函數(shù)聲明、變量聲明
    ```
    checkscopeContext = {
        AO: {
            arguments: {
                length: 0
            },
            scope2: undefined
        },
        Scope: checkscope.[[scope]],
    }
    ```

    5.第三步:將活動(dòng)對(duì)象壓入 checkscope 作用域鏈頂端
    ```
    checkscopeContext = {
        AO: {
            arguments: {
                length: 0
            },
            scope2: undefined
        },
        Scope: [AO, [[Scope]]]
    }
    ```

    6.準(zhǔn)備工作做完,開始執(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ù)引用 
因?yàn)椋?/code>

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

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多