欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文帶你掌握JavaScript中的執(zhí)行上下文和作用域

 更新時間:2023年02月08日 11:18:59   作者:mick  
作為一名前端工作人員,我們必須知道JavaScript內(nèi)部是如何執(zhí)行的。那對于執(zhí)行上下文和作用域的理解至關(guān)重要,無論是工作還是面試都是無法跳躍的一步,本文就來帶大家深入了解一下

執(zhí)行上下文

我們先來看段代碼

var foo = function () {
  console.log("foo1")
}

foo() // foo1

var foo = function () {
  console.log("foo2")
}
foo() // foo2

那這段代碼呢?

function foo() {
  console.log("foo1")
}
foo() // foo2

function foo() {
  console.log("foo2")
}
foo()// foo2

是不是有點懵逼了呢?第一段代碼比較好理解,但是第二段代碼為什么會打印兩個"foo2"呢?

這是因為JavaScript引擎并非一行一行分析和執(zhí)行程序的。當執(zhí)行一段代碼的時候,會有一些準備工作。那JavaScript引擎到底準備了哪些工作?

下面我們來一點點分析

console.log(a) // undefined
var a = 10

這段代碼我們在定義a之前打印了a,但是并沒有報錯,說明在執(zhí)行console.log(a)的時候,a就已經(jīng)被聲明了,也就是我們常說的變量提升,這就是準備工作。

var a
console.log(a)
a = 10

首先會把a的定義提前聲明,而不是賦值。

下面我們看下對于函數(shù)聲明和函數(shù)表達式,JavaScript引擎是如何做準備的。

console.log(add2(1, 2)) // 3
function add2(a, b) {
  return a + b
}

console.log(add1(1, 2)) // 報錯:add1 is not a function
var add1 = function (a, b) {
  return a + b
}

我們發(fā)現(xiàn),用函數(shù)語句創(chuàng)建的add2,函數(shù)名稱和函數(shù)體都被提前,在聲明它之前使用它。而函數(shù)表達式只是變量聲明提前了,變量賦值仍然在之前的位置?,F(xiàn)在回到剛開始那段代碼是不是就理解了呢?

所以JavaScript引擎都做好了哪些準備工作呢?

  • 變量、函數(shù)表達式——變量提前聲明,默認為undefined
  • 函數(shù)聲明——提前聲明并賦值

其實還有一個this也是提前就準備好了,并且也賦值了。

當執(zhí)行一個函數(shù)的時候,就會進行準備工作,這里的“準備工作”,就是“執(zhí)行上下文”

執(zhí)行上下文棧

執(zhí)行上下文棧管理執(zhí)行上下文。JavaScript代碼有兩種執(zhí)行上下文:全局執(zhí)行上下文和函數(shù)執(zhí)行上下文,還有一個是eval(我們先不考慮)。全局執(zhí)行上下文只有一個,函數(shù)執(zhí)行上下文是在每次函數(shù)執(zhí)行調(diào)用的時候,就會創(chuàng)建一個新的。

每個執(zhí)行上下文都有三個屬性:

  • 變量對象(Variable object, VO)
  • 作用域鏈(Scope chain
  • this

變量對象

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

不同執(zhí)行上下文的變量對象不同,下面來看看全局上下文的變量對象和函數(shù)上下文的變量對象

全局上下文

  • 全局對象是預(yù)定義的對象,作為JavaScript的全局函數(shù)和全局屬性的占位符。通過使用全局對象,可以訪問所有其他所有預(yù)定義對象、函數(shù)和屬性
  • 在頂層的JavaScript代碼中,可以用關(guān)鍵字this引用全局對象。因為全局對象是作用域鏈的頭,意味著所有非限定性的變量和函數(shù)名都會作為該對象的屬性來查詢
  • 例如,當JavaScript代碼引用parseInt()函數(shù)時,它引用的是全局對象的parseInt屬性。

函數(shù)上下文

在函數(shù)上下文中,我們用活動對象(activation object, AO)來表示變量對象。

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

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

執(zhí)行過程

執(zhí)行上下文的代碼會分成兩個階段進行處理:

  • 進入執(zhí)行上下文
  • 代碼執(zhí)行

進入執(zhí)行上下文

當調(diào)用函數(shù)后,進入執(zhí)行上下文,在執(zhí)行代碼之前,變量對象會包含:

函數(shù)的所有形參

  • 由名稱和對應(yīng)的值組成一個變量對象的屬性被創(chuàng)建
  • 沒有實參,屬性值設(shè)為undefined

函數(shù)聲明

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

變量聲明

  • 由名稱和對應(yīng)值(undefined)組成一個變量對象的屬性被創(chuàng)建
  • 如果變量名稱跟已經(jīng)聲明的形式參數(shù)或函數(shù)相同,則變量聲明不會干擾已經(jīng)存在的這類屬性 比如:
function foo(a) {
  var b = 2
  function c() {}
  var d = function () {}
  b = 3
}

foo(1)

進入執(zhí)行上下文后,AO的值:

AO={
    arguments: {
        0:1,
        length:1
    },
    a: 1,
    b:undefined,
    c: reference to function c(){},
    d:undefined
}

代碼執(zhí)行

在代碼執(zhí)行階段,會按照順序執(zhí)行代碼,根據(jù)代碼,修改變量對象的屬性的值

AO={
    arguments: {
        0:1,
        length:1
    },
    a: 1,
    b: 3,
    c: reference to function c(){},
    d: reference to FunctionExpression "d"
}

小小總結(jié)一下變量對象:

  • 全局上下文的變量對象初始化是全局對象
  • 函數(shù)上下文的變量對象初始化包括Arguments對象
  • 進入執(zhí)行上下文時會給變量對象添加形參,函數(shù)聲明,變量聲明等初始的屬性值
  • 在代碼執(zhí)行階段,會再次修改變量對象的屬性值。

下面我們看下執(zhí)行上下文棧是如何工作的

function fun3() {
  console.log("fun3")
}

function fun2() {
  fun3()
}

function fun1() {
  fun2()
}

fun1()

我們用數(shù)組模擬執(zhí)行上下文棧,最先遇到的是全局代碼,初始化的時候,會向執(zhí)行上下文棧中壓入全局執(zhí)行上下文globalContext

Stack=[
    globalContext
]

當執(zhí)行一個函數(shù)時候,就會創(chuàng)建一個執(zhí)行上下文,并且壓入執(zhí)行上下文棧中,當函數(shù)執(zhí)行完畢后,就會將函數(shù)的執(zhí)行上下文從棧中彈出。上下文所在其所有的代碼執(zhí)行完畢后會被銷毀。

// 執(zhí)行fun1
Stack.push(<fun1>functionContext);

// fun1中調(diào)用了fun2
Stack.push(<fun2>functionContext);

//fun2中調(diào)用了fun3
Stack.push(<fun3>functionContext);

//fun3執(zhí)行完畢 彈出
Stack.pop()

//fun2執(zhí)行完畢 彈出
Stack.pop()

//fun1執(zhí)行完畢 彈出
Stack.pop()

最后Stack底層永遠有個全局執(zhí)行上下文globalContext。

作用域

作用域是指程序源代碼中定義變量的區(qū)域。作用域規(guī)定了如何查找變量,也就是確定當前執(zhí)行代碼對變量的訪問權(quán)限。JavaScript采用詞法作用域,也就是靜態(tài)作用域。

靜態(tài)作用域和動態(tài)作用域

JavaScript采用的是詞法作用域,函數(shù)的作用域是在函數(shù)定義的時候決定的。詞法作用域相對的是動態(tài)作用域,函數(shù)的作用域是在函數(shù)調(diào)用的時候才決定的。

作用域鏈

查找變量的時候,會先從當前上下文的變量對象中查找,如果沒有找到就會從父級執(zhí)行上下文的變量對象中查找,一直找到全局上下文的變量對象,也就是全局對象。這樣由多個執(zhí)行上下文的變量對象構(gòu)成的鏈表就叫做作用域鏈。

函數(shù)創(chuàng)建

上面提到,函數(shù)的作用域在函數(shù)定義的時候就已經(jīng)決定了。這是因為函數(shù)有一個內(nèi)部屬性[[scope]],當函數(shù)創(chuàng)建的時候,就會保存所有父變量對象到其中,可以理解[[scope]]就是所有父變量對象的層級鏈,但是[[scope]]并不代表完整的作用域鏈。我們來看個代碼:

function foo(){
    function bar(){
    }
}

函數(shù)創(chuàng)建時,各自的[[scope]]為

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

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

當函數(shù)激活,進入函數(shù)體,創(chuàng)建VO/AO后,就會將活動對象添加到作用鏈的前端。

總結(jié)

執(zhí)行上下文和作用域的區(qū)別:

1.全局作用域除外,每個函數(shù)都會創(chuàng)建自己的作用域,作用域在函數(shù)定義時就已經(jīng)確定了,而不是在函數(shù)調(diào)用時。

全局執(zhí)行上下文環(huán)境是在全局作用域確定之后,js代碼馬上執(zhí)行之前創(chuàng)建的。

函數(shù)執(zhí)行上下文是在調(diào)用函數(shù)時,執(zhí)行函數(shù)體代碼之前創(chuàng)建的。

2.作用域是靜態(tài)的,只要函數(shù)定義好了就一直存在,且不會再變化。

執(zhí)行上下文環(huán)境是動態(tài)的,調(diào)用函數(shù)時創(chuàng)建,函數(shù)調(diào)用結(jié)束上下文環(huán)境就會被釋放。

以上就是一文帶你掌握JavaScript中的執(zhí)行上下文和作用域的詳細內(nèi)容,更多關(guān)于JavaScript執(zhí)行上下文 作用域的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JavaScript使用setTimeout實現(xiàn)倒計時效果

    JavaScript使用setTimeout實現(xiàn)倒計時效果

    這篇文章主要為大家詳細介紹了JavaScript使用setTimeout實現(xiàn)倒計時效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-02-02
  • 微信小程序checkbox組件使用詳解

    微信小程序checkbox組件使用詳解

    這篇文章主要介紹了微信小程序checkbox組件的使用,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • Element-plus安裝及基礎(chǔ)組件使用詳解

    Element-plus安裝及基礎(chǔ)組件使用詳解

    ElementPlus是一個基于Vue3的UI組件庫,旨在提供豐富的HTML元素封裝,以簡化前端開發(fā),主要特點包括預(yù)定義樣式、事件處理、易用性等,為開發(fā)者提供了一致且美觀的用戶界面,同時支持按需導(dǎo)入,提高項目效率,感興趣的朋友一起看看吧
    2024-09-09
  • 如何用CocosCreator實現(xiàn)射擊小游戲

    如何用CocosCreator實現(xiàn)射擊小游戲

    這篇文章主要介紹了如何用CocosCreator實現(xiàn)射擊小游戲,此游戲難度不大,僅作為入門的練手小游戲,一小時就能完成,里面用到的知識很常用,喜歡游戲的同學(xué),可以參考下
    2021-04-04
  • 淺談javascript六種數(shù)據(jù)類型以及特殊注意點

    淺談javascript六種數(shù)據(jù)類型以及特殊注意點

    這篇文章主要介紹了javascript六種數(shù)據(jù)類型以及特殊注意點,有需要的朋友可以參考一下
    2013-12-12
  • js腳本獲取webform服務(wù)器控件的方法

    js腳本獲取webform服務(wù)器控件的方法

    asp.net webform中獲取服務(wù)器控件,js腳本獲取服務(wù)器控件需要使用ClientID,下面有個示例,大家可以參考下
    2014-05-05
  • JavaScript實現(xiàn)簡單圖片切換

    JavaScript實現(xiàn)簡單圖片切換

    這篇文章主要為大家詳細介紹了JavaScript實現(xiàn)簡單圖片切換,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-04-04
  • JS訪問SWF的函數(shù)用法實例

    JS訪問SWF的函數(shù)用法實例

    這篇文章主要介紹了JS訪問SWF的函數(shù)用法,實例分析了javascript訪問swf文件的方法及易錯點的處理技巧,需要的朋友可以參考下
    2015-07-07
  • 原生JS和jQuery操作DOM對比總結(jié)

    原生JS和jQuery操作DOM對比總結(jié)

    這篇文章主要給大家介紹了原生JS和jQuery操作DOM的一些對比總結(jié),文中總結(jié)了很多的對比,相信對大家的學(xué)習(xí)或者工作能帶來一定的幫助,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-01-01
  • js實現(xiàn)帶圓角的多級下拉菜單效果

    js實現(xiàn)帶圓角的多級下拉菜單效果

    這篇文章主要介紹了js實現(xiàn)帶圓角的多級下拉菜單效果,通過調(diào)用封裝的js庫ocscript.js實現(xiàn)圓角下拉菜單功能,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-08-08

最新評論