首頁  >  文章  >  web前端  >  一文聊聊Javascript中的執行上下文

一文聊聊Javascript中的執行上下文

青灯夜游
青灯夜游轉載
2023-02-14 19:41:062382瀏覽

本篇文章帶大家聊聊Javascript中的執行上下文,分享一個思考題,透過對思考題的分析,想必會對執行上下文有更深入的理解。

一文聊聊Javascript中的執行上下文

在前面的幾篇文章中,我們深入了解了關於執行上下文的三個重要成員:變數物件、作用域鍊和this ,本篇文章是前四篇文章的的內容的集合,聚合分散的知識點,做一個簡單的鞏固。不知道有沒有人是上一篇來的,我們的上一篇留下了一個思考題,透過對思考題的分析,想必會對執行上下文有更深入的理解。

思考題

這裡為了稍微將案例複雜化一點,做了一點點修改,但是並沒有改變原題所考察的點。

function func(value){
    getValue = function(){
        console.log(value);
    };
    return this
}
            
function getValue(){
    console.log(5);
}

Func(1).getValue(); //为什么是1呢?

具體執行分析

執行全域程式碼,建立全域執行上下文,全域上下文被壓入執行上下文堆疊

ECStack = [ globalContext ];

初始化全域上下文

globalContext = {
    VO: {
        func: reference to function func(){},
        getValue: reference to function getValue(){}
    },
    Scope: [globalContext.VO],
    this: globalContext.VO //全局上下文
}

初始化全域上下文同時建立了兩個函數,因此也會保存他們父級作用域鏈在他們的內部屬性[[scope]] 內

func.[[scope]] = [
     globalContext.VO
];
getValue.[[scope]] = [
     globalContext.VO
];

此時開始執行程式碼,執行到最後的語句時先執行func 函數,也就建立以步驟func 函數執行上下文:

  • ##複製函數[[scope]] 屬性建立作用域連結

  • #用arguments 建立活動物件

  • 初始化活動物件

  • 將活動物件壓入checksfunccope 作用域鏈頂端。

  • 創建this,簡單分析:MemberExpression 值為func,func是函數對象,理所當然是一個Reference ,其中它的base value 是EnvironmentRecord ,所以它的this 值為ImplicitThisValue( ref),傳回值總是undefined ,非嚴格模式下,其值會被隱含轉換為全域物件。

  • funcContext = {
        AO: {
            arguments: { // 数组
                0: 1,
                length: 1
            }
        },
        Scope: [AO, globalContext.VO],
        this: undefined
    }
可能有人會有疑問,func 裡的 getValue 呢? ,因為它並沒有變數申明,因此他其實是一個屬性的賦值操作,在後面執行時才會被執行。

建立函數執行上下文後壓入執行上下文堆疊

    ECStack = [
        funcContext,
        globalContext
    ];

函數開始執行,此時就是

為什麼最後輸出是1的關鍵了,第一句賦值運算,那麼就需要沿著執行上下文去找變數getValue,那麼我們就來看funcContext 中的作用域,首先找到funcContext.AO 顯然並不存在getValue 這一屬性,那麼沿著作用域鏈往上找,找到了globalContext.VO ,找到了getValue ,這時候就會給全域作用域下的getValue 屬性重新賦值,賦的是函數的傳新版本,也重新建立了函數作用域,將這個全新的getValue 函數的父級作用域鏈保存在它在他們的內部屬性[[scope]] 內:

getValue .[[scope]] = [ funcContext.AO, globalContext.VO ];

然後才繼續返回this ,查找funcContext 的this ,即返回undefined;func 執行上下文出棧

ECStack = [ globalContext ];

繼續執行

Func(1).getValue(),前半部返回了undefined ,此時系統隱式轉換為全域變數對象,從全域變數物件中找到getValue 屬性。這時候我們發現getValue 早已不是當年那個少年,執行全新的getValue 的函數執行上下文併入棧:

getValueContext = {
    AO: {
        arguments: { // 数组
            length: 0
        }
    },
    Scope: [ AO, funcContext.AO, globalContext.VO ],
    this: undefined
} ECStack = [
    getValueContext,
    globalContext
 ];

函數開始執行,發現她要輸出

value ,沿著作用域去找,getValueContext.AO 中並沒有這個屬性, 繼續往下找找到funcContext.AO(注意! ),在形參中找到了value 那麼就輸出對樣的值,也就輸出了1。

函數執行完畢,getValueContext 和 globalContext 相繼出棧並銷毀,程式碼運行完畢。

總結

本片以一個簡單但又不簡單的範例,將前面的四篇文章串聯起來,完整地分析了JS程式碼執行時執行上下文的工作過程,希望大家對此能有更深的體會。但是,不知道有沒有細心的同學發現,上面的範例中,執行getValue 函數的過程中,由尋找屬性value的步驟(標記位置),那個時候func 函數明明已經執行完畢了,他的執行上下文已經出棧了,為什麼還能從他的執行上下文找到value 屬性呢?這其實就是閉包產生的原理了,下一篇我們還是用這個範例去學習閉包產生的原理。

【推薦學習:

javascript進階教學#

以上是一文聊聊Javascript中的執行上下文的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除