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

資訊專欄INFORMATION COLUMN

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

MonoLog / 2163人閱讀

摘要:變量對象作用域鏈因為變量對象在執(zhí)行上下文進入執(zhí)行階段時,就變成了活動對象,因此圖中使用了來表示。

作用域

作用域就是變量與函數(shù)的可訪問范圍,即作用域控制著變量與函數(shù)的可見性和生命周期。在 JavaScript 中,變量的作用域有全局作用域和局部作用域兩種。JavaScript 采用詞法作用域(lexical scoping),也就是靜態(tài)作用域。

靜態(tài)作用域

函數(shù)的作用域在函數(shù)定義的時候就決定了。
js函數(shù)有一個內(nèi)部屬性 [[scope]],當(dāng)函數(shù)創(chuàng)建的時候,就會保存所有父變量對象到其中,下文會詳細(xì)描述

動態(tài)作用域

函數(shù)的作用域是在函數(shù)調(diào)用的時候才決定的。

實例

靜態(tài)作用域的語言下面的代碼會打出1,因為在foo定義的時候,他的作用域就確定了在全局(后面講變量對象的時候也會說foo是注冊在全局的而不是在bar里面才注冊)

執(zhí)行 foo 函數(shù),先從 foo 函數(shù)內(nèi)部查找是否有局部變量 value,如果沒有,就根據(jù)書寫的位置,查找上面一層的代碼,也就是 value 等于 1,所以結(jié)果會打印 1。

var value = 1;

function foo() {
    console.log(value);
}

function bar() {
    var value = 2;
    foo();
}

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

就是當(dāng)前 JavaScript 代碼被解析和執(zhí)行時所在環(huán)境的抽象概念, JavaScript 中運行任何的代碼都是在執(zhí)行上下文中運行。

JavaScript代碼的整個執(zhí)行過程,分為兩個階段,代碼編譯階段與代碼執(zhí)行階段。

編譯階段由編譯器完成,將代碼翻譯成可執(zhí)行代碼,這個階段作用域規(guī)則會確定。

執(zhí)行階段由引擎完成,主要任務(wù)是執(zhí)行可執(zhí)行代碼,執(zhí)行上下文在這個階段創(chuàng)建。

執(zhí)行上下文創(chuàng)建和執(zhí)行:

執(zhí)行上下文有以下三個屬性

變量對象(Variable object,VO)

作用域鏈(Scope chain)

this

執(zhí)行上下文總共有三種類型:

全局執(zhí)行上下文: 這是默認(rèn)的、最基礎(chǔ)的執(zhí)行上下文。不在任何函數(shù)中的代碼都位于全局執(zhí)行上下文中。它做了兩件事:1. 創(chuàng)建一個全局對象,在瀏覽器中這個全局對象就是 window 對象。2. 將 this 指針指向這個全局對象。一個程序中只能存在一個全局執(zhí)行上下文。

函數(shù)執(zhí)行上下文: 每次調(diào)用函數(shù)時,都會為該函數(shù)創(chuàng)建一個新的執(zhí)行上下文。每個函數(shù)都擁有自己的執(zhí)行上下文,但是只有在函數(shù)被調(diào)用的時候才會被創(chuàng)建。一個程序中可以存在任意數(shù)量的函數(shù)執(zhí)行上下文。每當(dāng)一個新的執(zhí)行上下文被創(chuàng)建,它都會按照特定的順序執(zhí)行一系列步驟,具體過程將在本文后面討論。

Eval 函數(shù)執(zhí)行上下文: 運行在 eval 函數(shù)中的代碼也獲得了自己的執(zhí)行上下文(不常用)

執(zhí)行上下文棧

JavaScript 引擎創(chuàng)建了執(zhí)行上下文棧(Execution context stack,ECS)來管理執(zhí)行上下文
當(dāng) JavaScript 引擎首次讀取你的腳本時,它會創(chuàng)建一個全局執(zhí)行上下文并將其推入當(dāng)前的執(zhí)行棧。每當(dāng)發(fā)生一個函數(shù)調(diào)用,引擎都會為該函數(shù)創(chuàng)建一個新的執(zhí)行上下文并將其推到當(dāng)前執(zhí)行棧的頂端。
引擎會運行執(zhí)行上下文在執(zhí)行棧頂端的函數(shù),當(dāng)此函數(shù)運行完成后,其對應(yīng)的執(zhí)行上下文將會從執(zhí)行棧中彈出,上下文控制權(quán)將移到當(dāng)前執(zhí)行棧的下一個執(zhí)行上下文。

let a = "Hello World!";

function first() {  
  console.log("Inside first function");  
  second();  
  console.log("Again inside first function");  
}

function second() {  
  console.log("Inside second function");  
}

first();  
console.log("Inside Global Execution Context");

瀏覽器中加載時,JavaScript 引擎會創(chuàng)建一個全局執(zhí)行上下文并且將它推入當(dāng)前的執(zhí)行棧。

當(dāng)調(diào)用 first() 函數(shù)時,JavaScript 引擎為該函數(shù)創(chuàng)建了一個新的執(zhí)行上下文并將其推到當(dāng)前執(zhí)行棧的頂端。

當(dāng)在 first() 函數(shù)中調(diào)用 second() 函數(shù)時,創(chuàng)建了一個新的執(zhí)行上下文并將其推到當(dāng)前執(zhí)行棧的頂端。

當(dāng) second() 函數(shù)執(zhí)行完成后,它的執(zhí)行上下文從當(dāng)前執(zhí)行棧中彈出,上下文控制權(quán)將移到當(dāng)前執(zhí)行棧的下一個執(zhí)行上下文,即 first() 函數(shù)的執(zhí)行上下文。

當(dāng) first() 函數(shù)執(zhí)行完成后,它的執(zhí)行上下文從當(dāng)前執(zhí)行棧中彈出,上下文控制權(quán)將移到全局執(zhí)行上下文。

一旦所有代碼執(zhí)行完畢,Javascript 引擎把全局執(zhí)行上下文從執(zhí)行棧中移除。

// 偽代碼

ECStack = [
    globalContext
];

// first()
ECStack.push( functionContext);

// fun1中竟然調(diào)用了fun2,還要創(chuàng)建fun2的執(zhí)行上下文
ECStack.push( functionContext);


// second()執(zhí)行完畢
ECStack.pop(second);

// first()執(zhí)行完畢
ECStack.pop(first);


// 當(dāng)整個應(yīng)用程序結(jié)束的時候,ECStack 才會被清空,所以程序結(jié)束之前, ECStack 最底部永遠有個 globalContext:
變量對象 什么是變量對象

變量對象是與執(zhí)行上下文相關(guān)的數(shù)據(jù)作用域,存儲了在上下文中定義的變量和函數(shù)聲明。

什么是全局對象

全局對象是預(yù)定義的對象,作為 JavaScript 的全局函數(shù)和全局屬性的占位符。通過使用全局對象,可以訪問所有其他所有預(yù)定義的對象、函數(shù)和屬性。

在頂層 JavaScript 代碼中,可以用關(guān)鍵字 this 引用全局對象。因為全局對象是作用域鏈的頭,這意味著所有非限定性的變量和函數(shù)名都會作為該對象的屬性來查詢。

例如,當(dāng)JavaScript 代碼引用 parseInt() 函數(shù)時,它引用的是全局對象的 parseInt 屬性。全局對象是作用域鏈的頭,還意味著在頂層 JavaScript 代碼中聲明的所有變量都將成為全局對象的屬性。

可以通過 this 引用,在客戶端 JavaScript 中,全局對象就是 Window 對象。

console.log(this);// this 引用,在客戶端 JavaScript 中,全局對象就是 Window 對象。

console.log(this instanceof Object);//全局對象是由 Object 構(gòu)造函數(shù)實例化的一個對象。

console.log(Math.random());//.預(yù)定義了一堆,嗯,一大堆函數(shù)和屬性。
console.log(this.Math.random());

var a = 1;//作為全局變量的宿主。
console.log(this.a);
函數(shù)上下文

在函數(shù)上下文中,我們用活動對象(activation object, AO)來表示變量對象。
變量對象VO和活動對象AO是同一個對象在不同階段的表現(xiàn)形式。當(dāng)進入執(zhí)行環(huán)境的創(chuàng)捷階段時,變量對象被創(chuàng)建,這時變量對象的屬性無法被訪問。進入執(zhí)行階段后,變量對象被激活變成活動對象,此時活動對象的屬性可以被訪問。

函數(shù)執(zhí)行過程 進入執(zhí)行上下文,創(chuàng)建階段

當(dāng)進入執(zhí)行上下文時,這時候還沒有執(zhí)行代碼,在這個階段中,執(zhí)行上下文會分別創(chuàng)建變量對象,建立作用域鏈,以及確定this的指向。
變量對象會包括:

函數(shù)的所有形參 (如果是函數(shù)上下文)

函數(shù)聲明

變量聲明

function foo(a) {
  var b = 2;
  var c=3;
  function c() {}
  var d = function() {};

  b = 3;

}

foo(1);

根據(jù)函數(shù)參數(shù),創(chuàng)建并初始化arguments對象,及形參屬性

檢查上下文中的函數(shù)聲明,將函數(shù)名作為變量對象的屬性,函數(shù)引用作為值。如果該函數(shù)名在變量對象中已存在,則覆蓋已存在的函數(shù)引用。

檢查上下文的變量聲明,將變量名作為變量對象的屬性,值設(shè)置為undefined。如果該變量名在變量對象中已存在,為防止與函數(shù)名沖突,則跳過,不進行任何操作。

AO = {
    arguments: {
        0: 1,
        length: 1
    },
    a: 1,//注意a已經(jīng)初始化了
    b: undefined,
    c: reference to function c(){},//如果重名后調(diào)過了變量c只有函數(shù)c
    d: undefined
}
代碼執(zhí)行階段

上下文創(chuàng)建完成之后,就會開始執(zhí)行代碼,這個時候,會完成變量賦值,函數(shù)引用,以及執(zhí)行其他代碼。

AO = {
    arguments: {
        0: 1,
        length: 1
    },
    a: 1,
    b: 3,
    c: 3,//執(zhí)行階段c又會重新被賦值
    d: reference to FunctionExpression "d"
}
上下文總結(jié)

全局上下文的變量對象初始化是全局對象

函數(shù)上下文創(chuàng)建階段函數(shù)先注冊重名覆蓋,變量后注冊重名跳過

函數(shù)上下文的變量對象初始化只包括 Arguments 對象

在進入執(zhí)行上下文時會給變量對象添加形參、函數(shù)聲明、變量聲明等初始的屬性值,也就是初始化變量對象

在代碼執(zhí)行階段,會再次修改變量對象的屬性值

作用域鏈 什么是作用域鏈 定義

作用域鏈,是由當(dāng)前環(huán)境與上層環(huán)境的一系列變量對象組成,它保證了當(dāng)前執(zhí)行環(huán)境對符合訪問權(quán)限的變量和函數(shù)的有序訪問。

形成

上文的作用域中講到過函數(shù)的作用域在函數(shù)定義的時候就決定了,因為函數(shù)有一個內(nèi)部屬性 [[scope]],當(dāng)函數(shù)創(chuàng)建的時候,就會保存所有父變量對象到其中,當(dāng)查找變量的時候,會先從當(dāng)前上下文的變量對象中查找,如果沒有找到,就會從自己的scope中保存的父級(詞法層面上的父級)執(zhí)行上下文的變量對象中查找,一直找到全局上下文的變量對象,也就是全局對象。這樣由多個執(zhí)行上下文的變量對象構(gòu)成的鏈表就叫做作用域鏈。

區(qū)分作用域與作用域鏈 作用域

在JavaScript中,我們可以將作用域定義為一套規(guī)則,這套規(guī)則用來管理引擎如何在當(dāng)前作用域以及嵌套的子作用域中根據(jù)標(biāo)識符名稱進行變量查找。

兩者的區(qū)別

作用域是一套規(guī)則,那么作用域鏈?zhǔn)鞘裁茨??是這套規(guī)則的具體實現(xiàn)。

作用域規(guī)則在代碼編譯階段就確定了,而作用域鏈?zhǔn)窃趫?zhí)行上下文的創(chuàng)建階段生成的

舉個例子
var a = 20;

function test() {
    var b = 10;
   //function innerTest() {
   //     var c = 10;
   //     return b + c;
   //}
    return b;
}

test();

執(zhí)行過程
1.test 函數(shù)在全局上下文中被創(chuàng)建,保存全局上下文的變量對象組成的作用域鏈到內(nèi)部屬性[[scope]]

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

2.創(chuàng)建 test 函數(shù)執(zhí)行上下文,test函數(shù)執(zhí)行上下文被壓入執(zhí)行上下文棧

ECStack = [
    testContext,
    globalContext
];

3.test 函數(shù)并不立刻執(zhí)行,開始做準(zhǔn)備工作,第一步:復(fù)制[[scope]]屬性到函數(shù)上下文,創(chuàng)建了作用域鏈

testContext = {
    Scope: testscope.[[scope]],
}

4.第二步:用 arguments 創(chuàng)建活動對象,隨后初始化活動對象,加入形參、函數(shù)聲明、變量聲明

testscopeContext = {
    AO: {
        arguments: {
            length: 0
        },
        b: undefined
    },
    Scope: testscope.[[scope]],
}

5.第三步:將活動對象壓入 testscope 作用域鏈頂端

testscopeContext = {
    AO: {
        arguments: {
            length: 0
        },
        b: undefined
    },
    Scope: [AO, [[Scope]]]//用Scope簡寫testscope.[[scope]]
}

6.準(zhǔn)備工作做完,開始執(zhí)行函數(shù),隨著函數(shù)的執(zhí)行,修改 AO 的屬性值

testscopeContext = {
    AO: {
        arguments: {
            length: 0
        },
        b: 10
    },
    Scope: [AO, [[Scope]]]
}

7.查找到 b 的值,返回后函數(shù)執(zhí)行完畢,函數(shù)上下文從執(zhí)行上下文棧中彈出

ECStack = [
    globalContext
];

8.如果test內(nèi)部含有innerTest函數(shù),則在該innerTest函數(shù)創(chuàng)建時將test上下文中的作用域鏈傳入(testscopeContext.Scope)
然后后循環(huán)執(zhí)行和test相同的步驟

var a = 20;

function test() {
    var b = 10;
   function innerTest() {
        var c = 10;
        return b + c;
   }
    return b;
}

test();

全局,函數(shù)test,函數(shù)innerTest的執(zhí)行上下文先后創(chuàng)建。我們設(shè)定他們的變量對象分別為VO(global),VO(test), VO(innerTest)。而innerTest的作用域鏈,則同時包含了這三個變量對象,所以innerTest的執(zhí)行上下文可如下表示。

innerTestContext  = {
    AO: {...},  // 變量對象
    Scope: [VO(innerTest), VO(test), VO(global)], // 作用域鏈
}

因為變量對象在執(zhí)行上下文進入執(zhí)行階段時,就變成了活動對象,因此圖中使用了AO來表示。Active Object
作用域鏈?zhǔn)怯梢幌盗凶兞繉ο蠼M成,我們可以在這個單向通道中,查詢變量對象中的標(biāo)識符,這樣就可以訪問到上一層作用域中的變量了。

參考文章

https://github.com/mqyqingfen...

https://www.jianshu.com/p/21a...

https://juejin.im/post/5bdfd3...

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

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/105855.html

相關(guān)文章

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

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

    liangzai_cool 評論0 收藏0
  • 理解JavaScript中的作用作用

    摘要:示例當(dāng)一個函數(shù)創(chuàng)建后,它的作用域鏈會被創(chuàng)建此函數(shù)的作用域中可訪問的數(shù)據(jù)對象填充。每一個運行期上下文都和一個作用域鏈關(guān)聯(lián)。此時,作用域鏈中函數(shù)的所有局部變量所在的作用域?qū)ο髸煌坪螅L問代價變高了。 作用域 作用域就是變量與函數(shù)的可訪問范圍,即作用域控制著變量與函數(shù)的可見性和生命周期。在JavaScript中,變量的作用域有全局作用域和局部作用域兩種。 作用域鏈 函數(shù)對象有一個內(nèi)部屬性[...

    XanaHopper 評論0 收藏0
  • JavaScript深入之作用

    摘要:下面,讓我們以一個函數(shù)的創(chuàng)建和激活兩個時期來講解作用域鏈?zhǔn)侨绾蝿?chuàng)建和變化的。這時候執(zhí)行上下文的作用域鏈,我們命名為至此,作用域鏈創(chuàng)建完畢。 JavaScript深入系列第五篇,講述作用鏈的創(chuàng)建過程,最后結(jié)合著變量對象,執(zhí)行上下文棧,讓我們一起捋一捋函數(shù)創(chuàng)建和執(zhí)行的過程中到底發(fā)生了什么? 前言 在《JavaScript深入之執(zhí)行上下文?!分兄v到,當(dāng)JavaScript代碼執(zhí)行一段可執(zhí)行代...

    waltr 評論0 收藏0
  • 講清楚之javascript作用

    摘要:并且作用域鏈也確定了在當(dāng)前上下文中查找標(biāo)識符后返回的值。為了具象化分析問題,我們可以假設(shè)作用域鏈?zhǔn)且粋€數(shù)組,數(shù)組成員有一系列變量對象組成。注意,所有作用域鏈的最末端都為全局變量對象。所以作用域作用域鏈都是在當(dāng)前運行環(huán)境內(nèi)代碼執(zhí)行前就確定了。 什么是作用域(Scope)? 作用域產(chǎn)生于程序源代碼中定義變量的區(qū)域,在程序編碼階段就確定了。javascript 中分為全局作用域(Global...

    whidy 評論0 收藏0

發(fā)表評論

0條評論

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