亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

JavaScript基礎(chǔ)系列---執(zhí)行環(huán)境與作用域鏈

J4ck_Chan / 2586人閱讀

摘要:延長(zhǎng)作用域鏈下面兩種語(yǔ)句可以在作用域鏈的前端臨時(shí)增加一個(gè)變量對(duì)象以延長(zhǎng)作用域鏈,

問(wèn)題

今天看筆記發(fā)現(xiàn)自己之前記了一個(gè)關(guān)于同名標(biāo)識(shí)符優(yōu)先級(jí)的內(nèi)容,具體是下面這樣的:

形參優(yōu)先級(jí)高于當(dāng)前函數(shù)名,低于內(nèi)部函數(shù)名

形參優(yōu)先級(jí)高于arguments

形參優(yōu)先級(jí)高于只聲明卻未賦值的局部變量,但是低于聲明且賦值的局部變量

函數(shù)和變量都會(huì)聲明提升,函數(shù)名和變量名同名時(shí),函數(shù)名的優(yōu)先級(jí)要高。執(zhí)行代碼時(shí),同名函數(shù)會(huì)覆蓋只聲明卻未賦值的變量,但是它不能覆蓋聲明且賦值的變量

局部變量也會(huì)聲明提升,可以先使用后聲明,不影響外部同名變量

然后我就想,為什么會(huì)有這樣的優(yōu)先級(jí)呢,規(guī)定的?但是好像沒(méi)有這個(gè)規(guī)定,于是開(kāi)始查閱資料,就有了下文

初識(shí)Execution Context

Execution ContextJavascript中一個(gè)抽象概念,它定義了變量或函數(shù)有權(quán)訪問(wèn)的其他數(shù)據(jù),決定了它們各自的行為。為了便于理解,我們可以近似將其等同于執(zhí)行當(dāng)前代碼的環(huán)境,JavaScript的可執(zhí)行代碼包括

全局代碼

函數(shù)代碼

eval()代碼

每當(dāng)執(zhí)行流轉(zhuǎn)到這些可執(zhí)行代碼時(shí),就會(huì)“新建”一個(gè)Execution Context并進(jìn)入該Execution Context

在上圖中,共有4個(gè)Execution Context,其中有一個(gè)是Global Execution Context(有且僅有一個(gè)),還有三個(gè)Function Execution Context

再識(shí)Execution Context Stack

瀏覽器中的JavaScript解釋器是單線程的,每次創(chuàng)建并進(jìn)入一個(gè)新的Execution Context時(shí),這個(gè)Execution Context就會(huì)被推(push)進(jìn)一個(gè)環(huán)境棧中,這個(gè)棧稱為Execution Context Stack,當(dāng)當(dāng)前Execution Context的代碼執(zhí)行完之后,棧又會(huì)將其彈(pop)出,并銷毀這個(gè)Execution Context,保存在其中的變量及函數(shù)定義也隨之被銷毀,然后把控制權(quán)返回給之前的Execution ContextGlobal Execution Context例外,它要等到應(yīng)用程序退出后 —— 如關(guān)閉網(wǎng)頁(yè)或?yàn)g覽器 —— 才會(huì)被銷毀)

JavaScript的執(zhí)行流就是由這個(gè)機(jī)制控制的,以下面的代碼為例說(shuō)明:

var sayHello = "Hello";
function name(){
    var fisrtName = "Cao",
        lastName = "Cshine";
    function getFirstName(){
        return fisrtName;
    }
    function getLatName(){
        return lastName;
    }
    console.log(sayHello + getFirstName() + " " + getLastName());
}
name();

當(dāng)瀏覽器第一次加載script的時(shí)候,默認(rèn)會(huì)進(jìn)入Global Execution Context,所以Global Execution Context永遠(yuǎn)是在棧的最下面。

然后遇到函數(shù)調(diào)用name(),此時(shí)新建并進(jìn)入Function Execution Context name,Function Execution Context name入棧;

繼續(xù)執(zhí)行遇到函數(shù)調(diào)用getFirstName(),于是新建并進(jìn)入Function Execution Context getFirstName,Function Execution Context getFirstName入棧,由于該函數(shù)內(nèi)部不會(huì)再新建其他Execution Context,所以直接執(zhí)行完畢,然后出棧,控制權(quán)交給Function Execution Context name;

再往下執(zhí)行遇到函數(shù)調(diào)用getLastName(),于是新建并進(jìn)入Function Execution Context getLastName,Function Execution Context getLastName入棧,由于該函數(shù)內(nèi)部不會(huì)再新建其他Execution Context,所以直接執(zhí)行完畢,然后出棧,控制權(quán)交給Function Execution Context name;

執(zhí)行完console后,函數(shù)name也執(zhí)行完畢,于是出棧,控制權(quán)交給Function Execution Context name,至此棧中又只有Global Execution Context

關(guān)于Execution Context Stack有5個(gè)關(guān)鍵點(diǎn):

單線程

同步執(zhí)行(非異步)

1個(gè)Global Execution Context

無(wú)限制的函數(shù)Function Execution Context

每個(gè)函數(shù)調(diào)用都會(huì)創(chuàng)建新的Execution Context,即使是自己調(diào)用自己,如下面的代碼:

(function foo(i) {
    if (i === 3) {
        return;
    }
    else {
        foo(++i);
    }
}(0));

Execution Context Stack的情況如下圖所示:

親密接觸Execution Context

每個(gè)Execution Context在概念上可以看成由下面三者組成:

變量對(duì)象(Variable object,簡(jiǎn)稱VO

作用域鏈(Scope Chain

this

變量對(duì)象(Variable object

該對(duì)象與Execution Context相關(guān)聯(lián),保存著Execution Context中定義的所有變量、函數(shù)聲明以及函數(shù)形參,這個(gè)對(duì)象我們無(wú)法訪問(wèn),但是解析器在后臺(tái)處理數(shù)據(jù)是用到它(注意函數(shù)表達(dá)式以及沒(méi)用var/let/const聲明的變量不在VO中)

Global Execution Context中的變量對(duì)象VO根據(jù)宿主環(huán)境的不同而不同,在瀏覽器中為window對(duì)象,因此所有的全局變量和函數(shù)都是作為window對(duì)象的屬性和方法創(chuàng)建的。

對(duì)于Function Execution Context,變量對(duì)象VO為函數(shù)的活動(dòng)對(duì)象,活動(dòng)對(duì)象是在進(jìn)入Function Execution Context時(shí)創(chuàng)建的,它通過(guò)函數(shù)的arguments屬性初始化,也就是最初只包含arguments這一個(gè)屬性。

JavaScript解釋器內(nèi)部,每次調(diào)用Execution Context都會(huì)經(jīng)歷下面兩個(gè)階段:

創(chuàng)建階段(發(fā)生在函數(shù)調(diào)用時(shí),但是內(nèi)部代碼執(zhí)行前,這將解釋聲明提升現(xiàn)象)

創(chuàng)建作用域鏈(作用域鏈見(jiàn)下文)

創(chuàng)建變量對(duì)象VO

確定this的值

激活/代碼執(zhí)行階段

變量賦值、執(zhí)行代碼

其中創(chuàng)建階段的第二步創(chuàng)建變量對(duì)象VO的過(guò)程可以理解成下面這樣:

Global Execution Context中沒(méi)有這一步) 創(chuàng)建arguments對(duì)象,掃描函數(shù)的所有形參,并將形參名稱 和對(duì)應(yīng)值組成的鍵值對(duì)作為變量對(duì)象VO的屬性。如果沒(méi)有傳遞對(duì)應(yīng)的實(shí)參,將undefined作為對(duì)應(yīng)值。如果形參名為arguments,將覆蓋arguments對(duì)象

掃描Execution Context中所有的函數(shù)聲明(注意是函數(shù)聲明,函數(shù)表達(dá)式不算)

將函數(shù)名和對(duì)應(yīng)值(指向內(nèi)存中該函數(shù)的引用指針)組成組成的鍵值對(duì)作為變量對(duì)象VO的屬性

如果變量對(duì)象VO已經(jīng)存在同名的屬性,則覆蓋這個(gè)屬性

掃描Execution Context中所有的變量聲明

由變量名和對(duì)應(yīng)值(此時(shí)為undefined) 組成,作為變量對(duì)象的屬性

如果變量名與已經(jīng)聲明的形參或函數(shù)相同,此時(shí)什么都不會(huì)發(fā)生,變量聲明不會(huì)干擾已經(jīng)存在的這個(gè)同名屬性。

好~~現(xiàn)在我們來(lái)看代碼捋一遍:

function foo(num) {
    console.log(num);// 66
    console.log(a);// undefined
    console.log(b);// undefined
    console.log(fc);// f function fc() {}
    var a = "hello";
    var b = function fb() {};
    function fc() {}
}
foo(66);

當(dāng)調(diào)用foo(66)時(shí),創(chuàng)建階段時(shí),Execution Context可以理解成下面這個(gè)樣子

fooExecutionContext = {
    scopeChain: { ... },
    variableObject: {
        arguments: {
            0: 66,
            length: 1
        },
        num: 66,
        fc: pointer to function fc()
        a: undefined,
        b: undefined
    },
    this: { ... }
}

當(dāng)創(chuàng)建階段完成以后,執(zhí)行流進(jìn)入函數(shù)內(nèi)部,激活執(zhí)行階段,然后代碼完成執(zhí)行,Execution Context可以理解成下面這個(gè)樣子:

fooExecutionContext = {
    scopeChain: { ... },
    variableObject: {
        arguments: {
            0: 66,
            length: 1
        },
        num: 66,
        fc: pointer to function fc()
        a: "hello",
        b: pointer to function fb()
    },
    this: { ... }
}

作用域鏈(Scope Chain

當(dāng)代碼在一個(gè)Execution Context中執(zhí)行時(shí),就會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈,作用域鏈的用途是保證對(duì)執(zhí)行環(huán)境有權(quán)訪問(wèn)的所有變量和函數(shù)的有序訪問(wèn)

Global Execution Context中的作用域鏈只有Global Execution Context的變量對(duì)象(也就是window對(duì)象),而Function Execution Context中的作用域鏈還會(huì)有“父”Execution Context的變量對(duì)象,這里就會(huì)要牽扯到[[Scopes]]屬性,可以將函數(shù)作用域鏈理解為---- 當(dāng)前Function Execution Context的變量對(duì)象VO(也就是該函數(shù)的活動(dòng)對(duì)象AO) + [[Scopes]],怎么理解呢,我們繼續(xù)往下看

[[Scopes]]屬性

[[Scopes]]這個(gè)屬性與函數(shù)的作用域鏈有著密不可分的關(guān)系,JavaScript中每個(gè)函數(shù)都表示為一個(gè)函數(shù)對(duì)象,[[Scopes]]是函數(shù)對(duì)象的一個(gè)內(nèi)部屬性,只有JavaScript引擎可以訪問(wèn)。

結(jié)合函數(shù)的生命周期:

函數(shù)定義

[[Scopes]]屬性在函數(shù)定義時(shí)被存儲(chǔ),保持不變,直至函數(shù)被銷毀

[[Scopes]]屬性鏈接到定義該函數(shù)的作用域鏈上,所以他保存的是所有包含該函數(shù)的 “父/祖父/曾祖父...” Execution Context的變量對(duì)象(OV),我們將其稱為所有父變量對(duì)象(All POV

!!!特別注意 [[Scopes]]是在定義一個(gè)函數(shù)的時(shí)候決定的

函數(shù)調(diào)用

函數(shù)調(diào)用時(shí),會(huì)創(chuàng)建并進(jìn)入一個(gè)新的Function Execution Context,根據(jù)前面討論過(guò)的調(diào)用Function Execution Context的兩個(gè)階段可知:先創(chuàng)建作用域鏈,這個(gè)創(chuàng)建過(guò)程會(huì)將該函數(shù)對(duì)象的[[Scopes]]屬性加入到其中

然后會(huì)創(chuàng)建該函數(shù)的活動(dòng)對(duì)象AO(作為該Function Execution Context的變量對(duì)象VO),并將創(chuàng)建的這個(gè)活動(dòng)對(duì)象AO加到作用域鏈的最前端

然后確定this的值

正式執(zhí)行函數(shù)內(nèi)的代碼

通過(guò)上面的過(guò)程我們大概可以理解:作用域鏈 = 當(dāng)前Function Execution Context的變量對(duì)象VO(也就是該函數(shù)的活動(dòng)對(duì)象AO) + [[Scopes]],有了這個(gè)作用域鏈, 在發(fā)生標(biāo)識(shí)符解析的時(shí)候, 就會(huì)沿著作用域鏈一級(jí)一級(jí)地搜索標(biāo)識(shí)符,最開(kāi)始是搜索當(dāng)前Function Execution Context的變量對(duì)象VO,如果沒(méi)有找到,就會(huì)根據(jù)[[Scopes]]找到父變量對(duì)象,然后繼續(xù)搜索該父變量對(duì)象中是否有該標(biāo)識(shí)符;如果仍沒(méi)有找到,便會(huì)找到祖父變量對(duì)象并搜索其中是否有該標(biāo)識(shí)符;如此一級(jí)級(jí)的搜索,直至找到標(biāo)識(shí)符為止(如果直到最后也找不到,一般會(huì)報(bào)未定義的錯(cuò)誤);注意:對(duì)于thisarguments,只會(huì)搜到其本身的變量(活動(dòng))對(duì)象為止,而不會(huì)繼續(xù)按著作用域鏈搜素。

現(xiàn)在再結(jié)合例子來(lái)捋一遍:

var a = 10;
function foo(d) {
    var b = 20;
    function bar() {
        var c = 30;
        console.log(a +  b + c + d); // 110
        //這里可以訪問(wèn)a,b,c,d
    }
    //這里可以訪問(wèn)a,b,d 但是不能訪問(wèn)c
    bar();
}
//這里只能訪問(wèn)a
foo(50);

當(dāng)瀏覽器第一次加載script的時(shí)候,默認(rèn)會(huì)進(jìn)入Global Execution Context的創(chuàng)建階段

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

創(chuàng)建變量對(duì)象,此處為window對(duì)象。然后會(huì)掃描所有的全局函數(shù)聲明,再掃描全局變量聲明。之后該變量對(duì)象會(huì)加到Scope Chain

確定this的值

此時(shí)Global Execution Context可以表示為:

globalEC = {
    scopeChain: {
        pointer to globalEC.VO
    },
    VO: {
        a: undefined,
        foo: pointer to function foo(),
        (其他window屬性)
    },
    this: { ... }
}

接著進(jìn)入Global Execution Context的執(zhí)行階段

遇到賦值語(yǔ)句var a = 10,于是globalEC.VO.a = 10;

globalEC = {
    scopeChain: {
        pointer to globalEC.VO
    },
    VO: {
        a: 10,
        foo: pointer to function foo(),
        (其他window屬性)
    },
    this: { ... }
}

遇到foo函數(shù)定義語(yǔ)句,進(jìn)入foo函數(shù)的定義階段,foo[[Scopes]]屬性被確定

foo.[[Scopes]] = {
    pointer to globalEC.VO
}

遇到foo(50)調(diào)用語(yǔ)句,進(jìn)入foo函數(shù)調(diào)用階段,此時(shí)進(jìn)入Function Execution Context foo的創(chuàng)建階段

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

創(chuàng)建變量對(duì)象,此處為foo的活動(dòng)對(duì)象。先創(chuàng)建arguments對(duì)象,然后掃描函數(shù)的所有形參,之后會(huì)掃描foo函數(shù)內(nèi)所有的函數(shù)聲明,再掃描foo函數(shù)內(nèi)的變量聲明。之后該變量對(duì)象會(huì)加到Scope Chain

確定this的值

此時(shí)Function Execution Context foo可以表示為

fooEC = {
    scopeChain: {
        pointer to fooEC.VO,
        foo.[[Scopes]]
    },
    VO: {
        arguments: {
            0: 66,
            length: 1
        },
        b: undefined,
        d: 50,
        bar: pointer to function bar(),
    },
    this: { ... }
}

接著進(jìn)入Function Execution Context foo的執(zhí)行階段

遇到賦值語(yǔ)句var b = 20;,于是fooEC .VO.b = 20

fooEC = {
    scopeChain: {
        pointer to fooEC.VO,
        foo.[[Scopes]]
    },
    VO: {
        arguments: {
            0: 66,
            length: 1
        },
        b: 20,
        d: 50,
        bar: pointer to function bar(),
    },
    this: { ... }
}

遇到bar函數(shù)定義語(yǔ)句,進(jìn)入bar函數(shù)的定義階段,bar[[Scopes]]`屬性被確定

bar.[[Scopes]] = {
    pointer to fooEC.VO,
    pointer to globalEC.VO
}

遇到bar()調(diào)用語(yǔ)句,進(jìn)入bar函數(shù)調(diào)用階段,此時(shí)進(jìn)入Function Execution Context bar的創(chuàng)建階段

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

創(chuàng)建變量對(duì)象,此處為bar的活動(dòng)對(duì)象。先創(chuàng)建arguments對(duì)象,然后掃描函數(shù)的所有形參,之后會(huì)掃描foo函數(shù)內(nèi)所有的函數(shù)聲明,再掃描bar函數(shù)內(nèi)的變量聲明。之后該變量對(duì)象會(huì)加到Scope Chain

確定this的值

此時(shí)Function Execution Context bar可以表示為

barEC = {
   scopeChain: {
       pointer to barEC.VO,
       bar.[[Scopes]]
   },
   VO: {
       arguments: {
           length: 0
       },
       c: undefined
   },
   this: { ... }
}

接著進(jìn)入Function Execution Context bar的執(zhí)行階段

遇到賦值語(yǔ)句var c = 30,于是barEC.VO.c = 30;

barEC = {
    scopeChain: {
        pointer to barEC.VO,
        bar.[[Scopes]]
    },
    VO: {
        arguments: {
            length: 0
        },
        c: 30
    },
    this: { ... }
}

遇到打印語(yǔ)句console.log(a + b + c + d);,需要訪問(wèn)變量a,b,c,d

通過(guò)bar.[[Scopes]].globalEC.VO.a訪問(wèn)得到a=10

通過(guò)bar.[[Scopes]].fooEC.VO.b,bar.[[Scopes]].fooEC.VO.d訪問(wèn)得到b=20,d=50

通過(guò)barEC.VO.c訪問(wèn)得到c=30

通過(guò)運(yùn)算得出結(jié)果110

bar函數(shù)執(zhí)行完畢,Function Execution Context bar銷毀,變量c也隨之銷毀

foo函數(shù)執(zhí)行完畢,Function Execution Context foo銷毀,b,d,bar也隨之銷毀

所有代碼執(zhí)行完畢,等到該網(wǎng)頁(yè)被關(guān)閉或者瀏覽器被關(guān)閉,Global Execution Context才銷毀,a,foo才會(huì)銷毀

通過(guò)上面的例子,相信對(duì)Execution Context和作用域鏈的理解也更清楚了,下面簡(jiǎn)單總結(jié)一下作用域鏈:

作用域鏈的前端始終是當(dāng)前執(zhí)行的代碼所在Execution Context的變量對(duì)象;

下一個(gè)變量對(duì)象來(lái)自其包含Execution Context,以此類推;

最后一個(gè)變量對(duì)象始終是Global Execution Context的變量對(duì)象;

內(nèi)部Execution Context可通過(guò)作用域鏈訪問(wèn)外部Execution Context反之不可以;

標(biāo)識(shí)符解析是沿著作用域鏈一級(jí)一級(jí)地搜索標(biāo)識(shí)符的過(guò)程。搜索過(guò)程始終從作用域鏈的前端開(kāi)始,然后逐級(jí)的向后回溯,直到找到標(biāo)識(shí)符為止(如果找不到,通常會(huì)導(dǎo)致錯(cuò)誤);

作用域鏈的本質(zhì)是一個(gè)指向變量對(duì)象的指針列表,只引用而不實(shí)際包含變量對(duì)象。

延長(zhǎng)作用域鏈

下面兩種語(yǔ)句可以在作用域鏈的前端臨時(shí)增加一個(gè)變量對(duì)象以延長(zhǎng)作用域鏈,該變量對(duì)象會(huì)在代碼執(zhí)行后被移除

try-catch語(yǔ)句的catch
創(chuàng)建一個(gè)新的變量對(duì)象,其中包含的是被拋出的錯(cuò)誤對(duì)象的聲明

with語(yǔ)句
將指定的對(duì)象添加到作用域鏈中

function buildUrl(){
    var qs = "?debug=true";
    with(location){
        var url = href + qs;
    }
    //console.log(href) 將會(huì)報(bào)href is not defined的錯(cuò)誤,因?yàn)閣ith語(yǔ)句執(zhí)行完with創(chuàng)建的變量對(duì)象就被移除了
    return url;
}

with語(yǔ)句接收window.location對(duì)象,因此其變量對(duì)象就包含了window.location對(duì)象的所有屬性,而這個(gè)變量對(duì)象被添加到作用域鏈的前端。所以在with語(yǔ)句里面使用href相當(dāng)于window.location.href。

解答問(wèn)題

現(xiàn)在我們來(lái)解答最開(kāi)始的優(yōu)先級(jí)問(wèn)題

形參優(yōu)先級(jí)高于當(dāng)前函數(shù)名,低于內(nèi)部函數(shù)名

function fn(fn){
    console.log(fn);// cc
}
fn("cc");

函數(shù)fn屬于Global Execution Context,而形參fn屬于Function Execution Context fn,此時(shí)作用域的前端是Function Execution Context fn的變量對(duì)象,所以console.log(fn)為形參的值

function fa(fb){
    console.log(fb);// ? fb(){}
    function fb(){}
    console.log(fb);// ? fb(){}
}
fa("aaa");

調(diào)用fa函數(shù)時(shí),進(jìn)入Function Execution Context fa的創(chuàng)建階段,根據(jù)前面所說(shuō)的變量對(duì)象創(chuàng)建過(guò)程:

先創(chuàng)建arguments對(duì)象,然后掃描函數(shù)的所有形參,之后會(huì)掃描函數(shù)內(nèi)所有的函數(shù)聲明,再掃描函數(shù)內(nèi)的變量聲明;
掃描函數(shù)聲明時(shí),如果變量對(duì)象VO中已經(jīng)存在同名的屬性,則覆蓋這個(gè)屬性

我們可以得到fa的變量對(duì)象表示為:

fa.VO = {
    arguments: {
        0:"aaa",
        length: 1
    },
    fb: pointer to function fb(),
}

所以console.log(fb)得到的是fa.VO.fb的值? fb(){}

形參優(yōu)先級(jí)高于arguments

function fn(aa){
    console.log(arguments);// Arguments?["hello world"]
}
fn("hello world");

function fn(arguments){
    console.log(arguments);// hello world
}
fn("hello world");

調(diào)用fn函數(shù)時(shí),進(jìn)入Function Execution Context fn的創(chuàng)建階段,根據(jù)前面所說(shuō)的變量對(duì)象創(chuàng)建過(guò)程:

先創(chuàng)建arguments對(duì)象,然后掃描函數(shù)的所有形參,之后會(huì)掃描函數(shù)內(nèi)所有的函數(shù)聲明,再掃描函數(shù)內(nèi)的變量聲明;
先創(chuàng)建arguments對(duì)象,后掃描函數(shù)形參,如果形參名為arguments,將會(huì)覆蓋arguments對(duì)象

所以當(dāng)形參名為arguments時(shí),console.log(arguments)為形參的值hello world。

形參優(yōu)先級(jí)高于只聲明卻未賦值的局部變量,但是低于聲明且賦值的局部變量

function fa(aa){
    console.log(aa);//aaaaa
    var aa;
    console.log(aa);//aaaaa
}
fa("aaaaa");

調(diào)用fa函數(shù)時(shí),進(jìn)入Function Execution Context fa的創(chuàng)建階段,根據(jù)前面所說(shuō)的變量對(duì)象創(chuàng)建過(guò)程:

先創(chuàng)建arguments對(duì)象,然后掃描函數(shù)的所有形參,之后會(huì)掃描函數(shù)內(nèi)所有的函數(shù)聲明,再掃描函數(shù)內(nèi)的變量聲明;
掃描函數(shù)內(nèi)的變量聲明時(shí),如果變量名與已經(jīng)聲明的形參或函數(shù)相同,此時(shí)什么都不會(huì)發(fā)生,變量聲明不會(huì)干擾已經(jīng)存在的這個(gè)同名屬性

所以創(chuàng)建階段之后Function Execution Context fa的變量對(duì)象表示為:

fa.VO = {
    arguments: {
        0:"aaaaa",
        length: 1
    },
    aa:"aaaaa",
}

之后進(jìn)入Function Execution Context fa的執(zhí)行階段:console.log(aa);打印出fa.VO.aa(形參aa)的值aaaaa;由于var aa;僅聲明而未賦值,所以不會(huì)改變fa.VO.aa的值,所以下一個(gè)console.log(aa);打印出的仍然是fa.VO.aa(形參aa)的值aaaaa。

function fb(bb){
    console.log(bb);//bbbbb
    var bb = "BBBBB";
    console.log(bb);//BBBBB
}
fb("bbbbb");

調(diào)用fb函數(shù)時(shí),進(jìn)入Function Execution Context fb的創(chuàng)建階段,根據(jù)前面所說(shuō)的變量對(duì)象創(chuàng)建過(guò)程:

先創(chuàng)建arguments對(duì)象,然后掃描函數(shù)的所有形參,之后會(huì)掃描函數(shù)內(nèi)所有的函數(shù)聲明,再掃描函數(shù)內(nèi)的變量聲明;
掃描函數(shù)內(nèi)的變量聲明時(shí),如果變量名與已經(jīng)聲明的形參或函數(shù)相同,此時(shí)什么都不會(huì)發(fā)生,變量聲明不會(huì)干擾已經(jīng)存在的這個(gè)同名屬性

所以創(chuàng)建階段之后Function Execution Context fb的變量對(duì)象表示為:

fb.VO = {
    arguments: {
        0:"bbbbb",
        length: 1
    },
    bb:"bbbbb",
}

之后進(jìn)入Function Execution Context fb的執(zhí)行階段:console.log(bb);打印出fb.VO.bb(形參bb)的值"bbbbb";遇到var bb = "BBBBB";,fb.VO.bb的值將被賦為BBBBB,所以下一個(gè)console.log(bb);打印出fb.VO.bb(局部變量bb)的值BBBBB。

函數(shù)和變量都會(huì)聲明提升,函數(shù)名和變量名同名時(shí),函數(shù)名的優(yōu)先級(jí)要高。

console.log(cc);//? cc(){}
var cc = 1;
function cc(){}

根據(jù)Global Execution Context的創(chuàng)建階段中創(chuàng)建變量對(duì)象的過(guò)程:是先掃描函數(shù)聲明,再掃描變量聲明,且變量聲明不會(huì)影響已存在的同名屬性。所以在遇到var cc = 1;這個(gè)聲明語(yǔ)句之前,global.VO.cc? cc(){}

執(zhí)行代碼時(shí),同名函數(shù)會(huì)覆蓋只聲明卻未賦值的變量,但是它不能覆蓋聲明且賦值的變量

var cc = 1;
var dd;
function cc(){}
function dd(){}
console.log(cc);//1
console.log(dd);//? dd(){}

Global Execution Context的創(chuàng)建階段之后,Global Execution Context的變量對(duì)象可以表示為:

global.VO = {
    cc:pointer to function cc(),
    dd:pointer to function dd()
}

然后進(jìn)入Global Execution Context的執(zhí)行階段,遇到var cc = 1;這個(gè)聲明賦值語(yǔ)句后, global.VO.cc將被賦值為1;然后再遇到var dd這個(gè)聲明語(yǔ)句,由于僅聲明未賦值,所以不改變global.VO.dd的值;所以console.log(cc);打印出1,console.log(dd);打印出? dd(){}

局部變量也會(huì)聲明提升,可以先使用后聲明,不影響外部同名變量

每個(gè)Execution Context都會(huì)有變量創(chuàng)建這個(gè)過(guò)程,所以會(huì)有聲明提升;根據(jù)作用域鏈,如果局部變量與外部變量同名,那么最先找到的是局部變量,影響不到外部同名變量

相關(guān)資料

JavaScript基礎(chǔ)系列---變量及其值類型
Understanding Scope in JavaScript
What is the Execution Context & Stack in JavaScript?
深入探討JavaScript的執(zhí)行環(huán)境和棧
作用域原理
JavaScript執(zhí)行環(huán)境 + 變量對(duì)象 + 作用域鏈 + 閉包

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/95064.html

相關(guān)文章

  • 前端基礎(chǔ)進(jìn)階(四):詳細(xì)圖解作用域鏈閉包

    摘要:之前一篇文章我們?cè)敿?xì)說(shuō)明了變量對(duì)象,而這里,我們將詳細(xì)說(shuō)明作用域鏈。而的作用域鏈,則同時(shí)包含了這三個(gè)變量對(duì)象,所以的執(zhí)行上下文可如下表示。下圖展示了閉包的作用域鏈。其中為當(dāng)前的函數(shù)調(diào)用棧,為當(dāng)前正在被執(zhí)行的函數(shù)的作用域鏈,為當(dāng)前的局部變量。 showImg(https://segmentfault.com/img/remote/1460000008329355);初學(xué)JavaScrip...

    aikin 評(píng)論0 收藏0
  • JavaScript基礎(chǔ)系列---閉包及其應(yīng)用

    摘要:所以,有另一種說(shuō)法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。所以本文中將以維基百科中的定義為準(zhǔn)即在計(jì)算機(jī)科學(xué)中,閉包,又稱詞法閉包或函數(shù)閉包,是引用了自由變量的函數(shù)。 閉包(closure)是JavaScript中一個(gè)神秘的概念,許多人都對(duì)它難以理解,我也一直處于似懂非懂的狀態(tài),前幾天深入了解了一下執(zhí)行環(huán)境以及作用域鏈,可戳查看詳情,而閉包與作用域及作用域鏈的關(guān)系密不可分,所...

    leoperfect 評(píng)論0 收藏0
  • javascript系列--javascript引擎執(zhí)行的過(guò)程的理解--語(yǔ)法分析和預(yù)編譯階段

    摘要:所以覺(jué)得把這個(gè)執(zhí)行的詳細(xì)過(guò)程整理一下,幫助更好的理解。類似的語(yǔ)法報(bào)錯(cuò)的如下圖所示三預(yù)編譯階段代碼塊通過(guò)語(yǔ)法分析階段之后,語(yǔ)法都正確的下回進(jìn)入預(yù)編譯階段。另開(kāi)出新文章詳細(xì)分析,主要介紹執(zhí)行階段中的同步任務(wù)執(zhí)行和異步任務(wù)執(zhí)行機(jī)制事件循環(huán)。 一、概述 js是一種非常靈活的語(yǔ)言,理解js引擎的執(zhí)行過(guò)程對(duì)于我們學(xué)習(xí)js是非常有必要的??戳撕芏噙@方便文章,大多數(shù)是講的是事件循環(huán)(event loo...

    malakashi 評(píng)論0 收藏0
  • JavaScript-作用域-執(zhí)行上下文-變量對(duì)象-作用域鏈

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

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

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

    MonoLog 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<