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

一文帶你掌握J(rèn)avaScript中的執(zhí)行上下文和作用域

 更新時(shí)間:2023年02月08日 11:18:59   作者:mick  
作為一名前端工作人員,我們必須知道JavaScript內(nèi)部是如何執(zhí)行的。那對(duì)于執(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

是不是有點(diǎn)懵逼了呢?第一段代碼比較好理解,但是第二段代碼為什么會(huì)打印兩個(gè)"foo2"呢?

這是因?yàn)镴avaScript引擎并非一行一行分析和執(zhí)行程序的。當(dāng)執(zhí)行一段代碼的時(shí)候,會(huì)有一些準(zhǔn)備工作。那JavaScript引擎到底準(zhǔn)備了哪些工作?

下面我們來一點(diǎn)點(diǎn)分析

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

這段代碼我們?cè)诙xa之前打印了a,但是并沒有報(bào)錯(cuò),說明在執(zhí)行console.log(a)的時(shí)候,a就已經(jīng)被聲明了,也就是我們常說的變量提升,這就是準(zhǔn)備工作。

var a
console.log(a)
a = 10

首先會(huì)把a(bǔ)的定義提前聲明,而不是賦值。

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

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

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

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

所以JavaScript引擎都做好了哪些準(zhǔn)備工作呢?

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

其實(shí)還有一個(gè)this也是提前就準(zhǔn)備好了,并且也賦值了。

當(dāng)執(zhí)行一個(gè)函數(shù)的時(shí)候,就會(huì)進(jìn)行準(zhǔn)備工作,這里的“準(zhǔn)備工作”,就是“執(zhí)行上下文”

執(zhí)行上下文棧

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

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

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

變量對(duì)象

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

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

全局上下文

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

函數(shù)上下文

在函數(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ì)象。

執(zhí)行過程

執(zhí)行上下文的代碼會(huì)分成兩個(gè)階段進(jìn)行處理:

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

進(jìn)入執(zhí)行上下文

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

函數(shù)的所有形參

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

函數(shù)聲明

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

變量聲明

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

foo(1)

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

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

代碼執(zhí)行

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

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

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

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

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

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

function fun2() {
  fun3()
}

function fun1() {
  fun2()
}

fun1()

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

Stack=[
    globalContext
]

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

// 執(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底層永遠(yuǎn)有個(gè)全局執(zhí)行上下文globalContext。

作用域

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

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

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

作用域鏈

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

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

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

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

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

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

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

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

總結(jié)

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

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

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

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

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

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

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

相關(guān)文章

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

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

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

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

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

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

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

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

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

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

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

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

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

    JavaScript實(shí)現(xiàn)簡(jiǎn)單圖片切換

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

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

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

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

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

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

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

最新評(píng)論