自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

JavaScript中的執(zhí)行上下文和變量提升

開發(fā) 前端
與許多同類語(yǔ)言相比,JavaScript 是一種易于學(xué)習(xí)的編程語(yǔ)言。但是,如果您想理解、調(diào)試和編寫更好的代碼,則需要多加注意一些基本概念。

[[422216]]

本文轉(zhuǎn)載自微信公眾號(hào)「新鈦云服」,作者林泓輝 翻譯。轉(zhuǎn)載本文請(qǐng)聯(lián)系新鈦云服公眾號(hào)。

與許多同類語(yǔ)言相比,JavaScript 是一種易于學(xué)習(xí)的編程語(yǔ)言。但是,如果您想理解、調(diào)試和編寫更好的代碼,則需要多加注意一些基本概念。

在本文中,我們將了解兩個(gè)這樣的概念:

  • 執(zhí)行上下文
  • 變量提升

作為一個(gè)初學(xué)者的JavaScript,了解這些概念將有助于您了解this關(guān)鍵字,作用域和閉包。

JavaScript 中的執(zhí)行上下文

一般來(lái)說(shuō),一個(gè) JavaScript 源文件會(huì)有多行代碼。作為開發(fā)人員,我們將代碼組織成變量、函數(shù)、數(shù)據(jù)結(jié)構(gòu)(如對(duì)象和數(shù)組)等等。

語(yǔ)法環(huán)境決定了我們?nèi)绾我约霸诤翁幘帉懘a??纯聪旅娴拇a:

  1. function doSomething() { 
  2.  var age= 7; 
  3.  // Some more code 

在上面的代碼中,變量age在語(yǔ)法上位于函數(shù)內(nèi)部doSomething。

請(qǐng)注意,我們的代碼不會(huì)按原樣運(yùn)行。它必須由編譯器翻譯成計(jì)算機(jī)可理解的字節(jié)碼。通常,語(yǔ)法環(huán)境在您的代碼中會(huì)有多個(gè)。然而,并不是所有的環(huán)境都會(huì)同時(shí)執(zhí)行。

幫助代碼執(zhí)行的環(huán)境稱為執(zhí)行上下文。它是當(dāng)前正在運(yùn)行的代碼,以及有助于運(yùn)行它的一切??梢杂泻芏嗾Z(yǔ)法環(huán)境,但當(dāng)前運(yùn)行的代碼只能有一個(gè)執(zhí)行上下文。

查看下圖以了解語(yǔ)法環(huán)境和執(zhí)行上下文之間的區(qū)別:

語(yǔ)法環(huán)境與執(zhí)行上下文

那么在執(zhí)行上下文中到底發(fā)生了什么?代碼被逐行解析,生成可執(zhí)行的字節(jié)碼,分配內(nèi)存并執(zhí)行。

讓我們采用我們?cè)谏厦婵吹降南嗤瘮?shù)。您認(rèn)為執(zhí)行以下行時(shí)可能會(huì)發(fā)生什么?

  1. var age = 7; 

這段源代碼在最終執(zhí)行之前經(jīng)歷了以下階段:

  • 標(biāo)記:在此階段,源代碼字符串分解為多個(gè)有意義的塊,稱為Tokens. 例如,代碼var age = 7;標(biāo)記為var , age , = , 7和, ; .
  • 解析:下一個(gè)階段是解析,在這個(gè)階段,一個(gè)標(biāo)記數(shù)組變成一個(gè)由語(yǔ)言語(yǔ)法理解的嵌套元素樹。這棵樹被稱為AST(抽象語(yǔ)法樹)。
  • 代碼生成:在這個(gè)階段,在解析階段創(chuàng)建的 AST 變成可執(zhí)行的字節(jié)碼。該可執(zhí)行字節(jié)碼隨后由 JIT(即時(shí))編譯器進(jìn)一步優(yōu)化。

下面的動(dòng)畫圖片顯示了源代碼到可執(zhí)行字節(jié)碼的轉(zhuǎn)換。

可執(zhí)行字節(jié)碼的源代碼

所有這些事情都發(fā)生在一個(gè)執(zhí)行上下文中。所以執(zhí)行上下文是代碼的特定部分的執(zhí)行環(huán)境。

有兩種類型的執(zhí)行上下文:

  • 全局執(zhí)行上下文 (GEC)
  • 函數(shù)執(zhí)行上下文 (FEC)

每個(gè)執(zhí)行上下文都有兩個(gè)階段:

  • 創(chuàng)建階段
  • 執(zhí)行階段

讓我們?cè)敿?xì)看看它們中的每一個(gè),并更好地理解它們。

JavaScript 中的全局執(zhí)行上下文 (GEC)

每當(dāng)我們執(zhí)行 JavaScript碼時(shí),它都會(huì)創(chuàng)建一個(gè)全局執(zhí)行上下文(也稱為基本執(zhí)行上下文)。全局執(zhí)行上下文有兩個(gè)階段。

創(chuàng)建階段

在創(chuàng)建階段,創(chuàng)建了兩個(gè)獨(dú)特的東西:

  • 調(diào)用的全局對(duì)象window(用于客戶端 JavaScript)。
  • 一個(gè)名為this的變量。

如果代碼中聲明了任何變量,則會(huì)為該變量分配內(nèi)存。該變量使用唯一key進(jìn)行初始化,并賦值為undefined。

如果代碼中有function ,它會(huì)被直接放入內(nèi)存中。我們將在Hoisting后面的部分中詳細(xì)了解這部分。

執(zhí)行階段

代碼執(zhí)行在這個(gè)階段開始。在這里進(jìn)行全局變量的賦值。請(qǐng)注意,這里沒(méi)有調(diào)用函數(shù),因?yàn)樗l(fā)生在函數(shù)執(zhí)行上下文中。我們將在后面討論這個(gè)問(wèn)題。

讓我們通過(guò)幾個(gè)例子來(lái)理解這兩個(gè)階段。

示例 1:加載空腳本

創(chuàng)建一個(gè)名為index.js的空 JavaScript 文件及一個(gè)包含以下內(nèi)容的 HTML 文件:

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.    <meta charset="UTF-8"
  5.    <meta http-equiv="X-UA-Compatible" content="IE=edge"
  6.    <meta name="viewport" content="width=device-width, initial-scale=1.0"
  7.    <title>Document</title> 
  8.    <script src='./index.js'></script> 
  9. </head> 
  10. <body> 
  11.   I'm loading an empty script 
  12. </body> 
  13. </html> 

我們使用<script>標(biāo)簽將空腳本文件導(dǎo)入到 HTML 文件中。

在瀏覽器中加載 HTML 文件并打開 Chrome DevTools(快捷鍵通常為F12)或其他瀏覽器也是可以的。選擇console選項(xiàng)卡,鍵入window并按回車鍵。您可以看到瀏覽器的Window對(duì)象。

windows對(duì)象

現(xiàn)在,輸入this并按回車鍵。您可以看到和Window對(duì)象一樣的this對(duì)象。

'this' 的值

如果您輸入window === this則會(huì)得到返回值true

好的,那么我們學(xué)到了什么?

  • 當(dāng)我們加載 JavaScript 文件時(shí),即使它是空的,也會(huì)創(chuàng)建全局執(zhí)行上下文。
  • 它在創(chuàng)建階段為我們創(chuàng)建了兩個(gè)特殊的東西,即window對(duì)象和this。
  • 在全局執(zhí)行上下文中,window對(duì)象 和this是相等的。
  • 因?yàn)槟_本文件是空白的,所以沒(méi)有什么可以執(zhí)行的。所以在執(zhí)行階段什么也不會(huì)發(fā)生。

示例 2:使用變量和函數(shù)

現(xiàn)在讓我們看一個(gè)在 JavaScript 文件中包含一些代碼的示例。我們將添加一個(gè)變量blog,并為其分配一個(gè)值。我們還將定義一個(gè)名為logBlog的函數(shù)。

  1. var blog = 'freeCodeCamp'
  2.  
  3. function logBlog() { 
  4.  console.log(this.blog);  

在創(chuàng)建階段:

  • 全局對(duì)象window和變量this被創(chuàng)建。
  • 內(nèi)存被分配給變量blog和函數(shù)logBlog。
  • 該變量blog由一個(gè)特殊值undefined初始化。該函數(shù)logBlog直接放置在內(nèi)存中。

在執(zhí)行階段:

  • 值freeCodeCamp被分配給變量blog。
  • 由于我們已經(jīng)定義了函數(shù)但還沒(méi)有調(diào)用它,因此函數(shù)執(zhí)行不會(huì)發(fā)生。我們將調(diào)用該函數(shù),看看當(dāng)我們了解函數(shù)執(zhí)行上下文時(shí)會(huì)發(fā)生什么。

JavaScript 中的函數(shù)執(zhí)行上下文 (FEC)

當(dāng)我們調(diào)用一個(gè)函數(shù)時(shí),會(huì)創(chuàng)建一個(gè)函數(shù)執(zhí)行上下文。讓我們擴(kuò)展上面使用的相同示例,但這次我們將調(diào)用該函數(shù)。

  1. var blog = 'freeCodeCamp'
  2.  
  3. function logBlog() { 
  4.  console.log(this.blog);  
  5.  
  6. // Let us call the function 
  7. logBlog(); 

函數(shù)執(zhí)行上下文經(jīng)歷相同的階段,即創(chuàng)建和執(zhí)行。

函數(shù)執(zhí)行階段可以訪問(wèn)一個(gè)名為arguments的特殊值。它是傳遞給函數(shù)的參數(shù)。但在我們的示例中,沒(méi)有傳遞任何參數(shù)。

請(qǐng)注意,在全局執(zhí)行上下文中創(chuàng)建的window對(duì)象和this變量仍然可以在此上下文中訪問(wèn)。

當(dāng)一個(gè)函數(shù)調(diào)用另一個(gè)函數(shù)時(shí),會(huì)為新的函數(shù)調(diào)用創(chuàng)建一個(gè)新的函數(shù)執(zhí)行上下文。每個(gè)函數(shù)中相應(yīng)的變量只能在對(duì)應(yīng)的執(zhí)行上下文中使用。

在 JavaScript 中的變量提升

讓我們轉(zhuǎn)到另一個(gè)基本概念Hoisting。當(dāng)我第一次聽說(shuō)Hoisting時(shí),花了一些時(shí)間才理解這個(gè)意思。

在英語(yǔ)中,hoisting 的意思是使用繩索和滑輪提升某物。這可能會(huì)誤導(dǎo)您認(rèn)為 JavaScript 引擎會(huì)在特定代碼執(zhí)行階段拉取變量和函數(shù)。接下來(lái),讓我們理解Hoisting的意思。

JavaScript 中的變量提升

請(qǐng)看下面的例子并猜測(cè)輸出:

  1. console.log(name); 
  2. var name;    // undefined 

然而,為什么是undefined?如果我們?cè)谄渌幊陶Z(yǔ)言中使用類似的代碼。在這種情況下,我們將在控制臺(tái)得到報(bào)錯(cuò),指出該變量name未聲明,而我們正試圖在此之前訪問(wèn)它。但是在JavaScript的執(zhí)行上下文里:

在創(chuàng)建階段,

  • 內(nèi)存被分配給變量name,并且
  • 一個(gè)特殊的值undefined被分配給變量。

在執(zhí)行階段,

該console.log(name)語(yǔ)句將執(zhí)行。

這種為變量分配內(nèi)存并賦值為undefined在執(zhí)行上下文的創(chuàng)建階段使用值進(jìn)行初始化的機(jī)制稱為Variable Hoisting(變量提升)。

特殊值undefined意味著聲明了一個(gè)變量但沒(méi)有賦值。

如果我們?yōu)樽兞糠峙湟粋€(gè)這樣的值:

  1. name = 'freeCodeCamp'

執(zhí)行階段會(huì)將這個(gè)值賦給變量。

JavaScript 中的函數(shù)提升

現(xiàn)在讓我們談?wù)凢unction Hoisting(函數(shù)提升)。它與Variable Hoisting的模式相同。

執(zhí)行上下文的創(chuàng)建階段將函數(shù)聲明放入內(nèi)存,并在執(zhí)行階段執(zhí)行。請(qǐng)看下面的例子:

  1. // Invoke the function functionA 
  2. functionA(); 
  3.  
  4. // Declare the function functionA 
  5. function functionA() { 
  6. console.log('Function A'); 
  7. // Invoke the function FunctionB     
  8. functionB(); 
  9.  
  10. // Declare the function FunctionB 
  11. function functionB() { 
  12. console.log('Function B'); 

輸出如下:

  1. Function A 
  2. Function B 

執(zhí)行上下文為函數(shù)創(chuàng)建內(nèi)存并將整個(gè)函數(shù)聲明functionA放入其中。

函數(shù)創(chuàng)建自己的執(zhí)行上下文。所以類似的事情也發(fā)生了functionB。

接下來(lái),函數(shù)分別在它們的執(zhí)行上下文中執(zhí)行。

在創(chuàng)建階段將整個(gè)函數(shù)聲明提前放入內(nèi)存稱為Function Hoisting。

一些基本規(guī)則

既然我們了解了變量提升的概念,那么讓我們了解一些基本規(guī)則:

  • 在代碼中使用變量和函數(shù)之前,務(wù)必先定義它們。這將減少意外的錯(cuò)誤,為您的調(diào)試減少麻煩。
  • 提升僅用于函數(shù)聲明,而不用于初始化。這是一個(gè)函數(shù)初始化的例子,代碼執(zhí)行會(huì)中斷。
  1. logMe(); 
  2.  
  3. var logMe = function() { 
  4.  console.log('Logging...'); 

代碼執(zhí)行將中斷,因?yàn)樵诤瘮?shù)初始化時(shí),變量logMe將作為變量而不是函數(shù)被提升。因此,對(duì)于變量提升,內(nèi)存分配將在初始化時(shí)發(fā)生undefined。這就是我們會(huì)得到錯(cuò)誤的原因:

函數(shù)初始化時(shí)出錯(cuò)

假設(shè)我們嘗試在聲明之前訪問(wèn)一個(gè)變量,然后使用letandconst關(guān)鍵字來(lái)聲明它。在這種情況下,它們將被提升但不會(huì)被分配默認(rèn)值undefined。訪問(wèn)此類變量將導(dǎo)致ReferenceError. 下面是一個(gè)例子:

  1. console.log(name); 
  2. let name

它會(huì)拋出錯(cuò)誤:

使用 let 和 const 關(guān)鍵字聲明的提升變量時(shí)出錯(cuò)

如果我們使用var代替let和,相同的代碼將毫無(wú)問(wèn)題地運(yùn)行const。這個(gè)錯(cuò)誤是因?yàn)樾碌腏avaScript 語(yǔ)言的保護(hù)機(jī)制,防止意外提升可能會(huì)導(dǎo)致不必要的麻煩。

感謝您能看到最后,我希望這篇文章能幫助您更好的理解JavaScript中的執(zhí)行上下文與變量提升的機(jī)制。

原文:https://www.freecodecamp.org/news/javascript-execution-context-and-hoisting/

 

責(zé)任編輯:武曉燕 來(lái)源: 新鈦云服
相關(guān)推薦

2022-09-14 13:13:51

JavaScript上下文

2019-03-14 08:00:00

JavaScript執(zhí)行棧前端

2017-05-11 14:00:02

Flask請(qǐng)求上下文應(yīng)用上下文

2020-07-24 10:00:00

JavaScript執(zhí)行上下文前端

2021-05-27 07:02:05

JavaScript代碼設(shè)施

2025-04-07 01:02:00

GoAPI語(yǔ)言

2024-04-07 08:50:00

谷歌框架

2012-12-31 10:01:34

SELinuxSELinux安全

2012-07-18 11:39:18

ibmdw

2015-07-08 10:25:05

Javascript上下文作用域

2015-10-09 09:43:28

CSS CSS3

2022-09-15 08:01:14

繼承基礎(chǔ)設(shè)施基礎(chǔ)服務(wù)

2023-07-11 10:02:23

2020-06-22 08:41:34

JS語(yǔ)言代碼

2022-04-24 15:37:26

LinuxCPU

2024-03-14 08:11:45

模型RoPELlama

2023-05-05 07:41:42

執(zhí)行上下文JavaScript

2024-09-30 14:10:00

2025-03-18 08:14:05

2017-12-17 17:01:23

限界上下文系統(tǒng)模型
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)