中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

javascript變量提升的相關知識有哪些

發布時間:2022-02-24 09:35:08 來源:億速云 閱讀:150 作者:小新 欄目:web開發

這篇文章將為大家詳細講解有關javascript變量提升的相關知識有哪些,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

咱們先看段代碼,你覺得下面這段代碼輸出的結果是什么?

showName()
console.log(myname)
var myname = '極客時間'
function showName() {
    console.log('函數showName被執行');
}

使用過 JavaScript 開發的程序員應該都知道,JavaScript 是按順序執行的。若按照這個邏輯來理解的話,那么:

  • 當執行到第 1 行的時候,由于函數 showName 還沒有定義,所以執行應該會報錯;

  • 同樣執行第 2 行的時候,由于變量 myname 也未定義,所以同樣也會報錯。

然而實際執行結果卻并非如此, 如下圖:
javascript變量提升的相關知識有哪些
第 1 行輸出“函數 showName 被執行”,第 2 行輸出“undefined”,這和前面想象中的順序執行有點不一樣啊!

通過上面的執行結果,你應該已經知道了函數或者變量可以在定義之前使用,那如果使用沒有定義的變量或者函數,JavaScript 代碼還能繼續執行嗎?為了驗證這點,我們可以刪除第 3 行變量 myname 的定義,如下所示:

showName()
console.log(myname)
function showName() {
    console.log('函數showName被執行');
}

然后再次執行這段代碼時,JavaScript 引擎就會報錯,結果如下:
javascript變量提升的相關知識有哪些
從上面兩段代碼的執行結果來看,我們可以得出如下三個結論:

  • 在執行過程中,若使用了未聲明的變量,那么 JavaScript 執行會報錯。

  • 在一個變量定義之前使用它,不會出錯,但是該變量的值會為 undefined,而不是定義時的值。

  • 在一個函數定義之前使用它,不會出錯,且函數能正確執行。

第一個結論很好理解,因為變量沒有定義,這樣在執行 JavaScript 代碼時,就找不到該變量,所以 JavaScript 會拋出錯誤。

但是對于第二個和第三個結論,就挺讓人費解的:

  • 變量和函數為什么能在其定義之前使用?這似乎表明 JavaScript 代碼并不是一行一行執行的。

  • 同樣的方式,變量和函數的處理結果為什么不一樣?比如上面的執行結果,提前使用的 showName 函數能打印出來完整結果,但是提前使用的 myname 變量值卻是 undefined,而不是定義時使用的“極客時間”這個值。

變量提升(Hoisting)

要解釋這兩個問題,你就需要先了解下什么是變量提升。

不過在介紹變量提升之前,我們先通過下面這段代碼,來看看什么是 JavaScript 中的聲明和賦值。

var myname = '極客時間'

這段代碼你可以把它看成是兩行代碼組成的:

var myname    //聲明部分
myname = '極客時間'  //賦值部分

如下圖所示:
javascript變量提升的相關知識有哪些
上面是變量的聲明和賦值,那接下來我們再來看看函數的聲明和賦值,結合下面這段代碼:

function foo(){
  console.log('foo')
}

var bar = function(){
  console.log('bar')
}

第一個函數 foo 是一個完整的函數聲明,也就是說沒有涉及到賦值操作;第二個函數是先聲明變量 bar,再把function(){console.log(‘bar’)}賦值給 bar。為了直觀理解,你可以參考下圖:
javascript變量提升的相關知識有哪些
好了,理解了聲明和賦值操作,那接下來我們就可以聊聊什么是變量提升了。

所謂的變量提升,是指在 JavaScript 代碼執行過程中,JavaScript 引擎把變量的聲明部分和函數的聲明部分提升到代碼開頭的“行為”。變量被提升后,會給變量設置默認值,這個默認值就是我們熟悉的 undefined。

下面我們來模擬下實現:

/*
* 變量提升部分
*/// 把變量 myname提升到開頭,// 同時給myname賦值為undefinedvar myname = undefined// 把函數showName提升到開頭function showName() {
    console.log('showName被調用');}/*
 * 可執行代碼部分
*/showName()console.log(myname)// 去掉var聲明部分,保留賦值語句myname = '極客時間'

為了模擬變量提升的效果,我們對代碼做了以下調整,如下圖:
javascript變量提升的相關知識有哪些
從圖中可以看出,對原來的代碼主要做了兩處調整:

  • 第一處是把聲明的部分都提升到了代碼開頭,如變量 myname 和函數 showName,并給變量設置默認值 undefined;

  • 第二處是移除原本聲明的變量和函數,如var myname = '極客時間’的語句,移除了 var 聲明,整個移除 showName 的函數聲明。

通過這兩步,就可以實現變量提升的效果。你也可以執行這段模擬變量提升的代碼,其輸出結果和第一段代碼應該是完全一樣的。

通過這段模擬的變量提升代碼,相信你已經明白了可以在定義之前使用變量或者函數的原因——函數和變量在執行之前都提升到了代碼開頭

JavaScript 代碼的執行流程

從概念的字面意義上來看,“變量提升”意味著變量和函數的聲明會在物理層面移動到代碼的最前面,正如我們所模擬的那樣。但,這并不準確。實際上變量和函數聲明在代碼里的位置是不會改變的,而且是在編譯階段被 JavaScript 引擎放入內存中。對,你沒聽錯,一段 JavaScript 代碼在執行之前需要被 JavaScript 引擎編譯,編譯完成之后,才會進入執行階段。大致流程你可以參考下圖:
javascript變量提升的相關知識有哪些

1. 編譯階段

那么編譯階段和變量提升存在什么關系呢?

為了搞清楚這個問題,我們還是回過頭來看上面那段模擬變量提升的代碼,為了方便介紹,可以把這段代碼分成兩部分。

第一部分:變量提升部分的代碼。

var myname = undefined
function showName() {
    console.log('函數showName被執行');
}

第二部分:執行部分的代碼。

showName()
console.log(myname)
myname = '極客時間'

下面我們就可以把 JavaScript 的執行流程細化,如下圖所示:

javascript變量提升的相關知識有哪些
從上圖可以看出,輸入一段代碼,經過編譯后,會生成兩部分內容:執行上下文(Execution context)和可執行代碼。

執行上下文是 JavaScript 執行一段代碼時的運行環境,比如調用一個函數,就會進入這個函數的執行上下文,確定該函數在執行期間用到的諸如 this、變量、對象以及函數等。

關于執行上下文的細節,我會在下一篇文章《08 | 調用棧:為什么 JavaScript 代碼會出現棧溢出?》做詳細介紹,現在你只需要知道,在執行上下文中存在一個變量環境的對象(Viriable Environment),該對象中保存了變量提升的內容,比如上面代碼中的變量 myname 和函數 showName,都保存在該對象中。

你可以簡單地把變量環境對象看成是如下結構:

VariableEnvironment:
     myname -> undefined, 
     showName ->function : {console.log(myname)

了解完變量環境對象的結構后,接下來,我們再結合下面這段代碼來分析下是如何生成變量環境對象的。

showName()
console.log(myname)
var myname = '極客時間'
function showName() {
    console.log('函數showName被執行');
}

我們可以一行一行來分析上述代碼:

  • 第 1 行和第 2 行,由于這兩行代碼不是聲明操作,所以 JavaScript 引擎不會做任何處理;

  • 第 3 行,由于這行是經過 var 聲明的,因此 JavaScript 引擎將在環境對象中創建一個名為 myname 的屬性,并使用 undefined 對其初始化;

  • 第 4 行,JavaScript 引擎發現了一個通過 function 定義的函數,所以它將函數定義存儲到堆 (HEAP)中,并在環境對象中創建一個 showName 的屬性,然后將該屬性值指向堆中函數的位置(不了解堆也沒關系,JavaScript 的執行堆和執行棧我會在后續文章中介紹)。

這樣就生成了變量環境對象。接下來 JavaScript 引擎會把聲明以外的代碼編譯為字節碼,至于字節碼的細節,我也會在后面文章中做詳細介紹,你可以類比如下的模擬代碼:

showName()
console.log(myname)
myname = '極客時間'

好了,現在有了執行上下文和可執行代碼了,那么接下來就到了執行階段了。

2. 執行階段

JavaScript 引擎開始執行“可執行代碼”,按照順序一行一行地執行。下面我們就來一行一行分析下這個執行過程:

  • 當執行到 showName 函數時,JavaScript 引擎便開始在變量環境對象中查找該函數,由于變量環境對象中存在該函數的引用,所以 JavaScript 引擎便開始執行該函數,并輸出“函數 showName 被執行”結果。

  • 接下來打印“myname”信息,JavaScript 引擎繼續在變量環境對象中查找該對象,由于變量環境存在 myname 變量,并且其值為 undefined,所以這時候就輸出 undefined。

  • 接下來執行第 3 行,把“極客時間”賦給 myname 變量,賦值后變量環境中的 myname 屬性值改變為“極客時間”,變量環境如下所示:

VariableEnvironment:
     myname -> "極客時間", 
     showName ->function : {console.log(myname)

好了,以上就是一段代碼的編譯和執行流程 。

代碼中出現相同的變量或者函數怎么辦?

現在你已經知道了,在執行一段 JavaScript 代碼之前,會編譯代碼,并將代碼中的函數和變量保存到執行上下文的變量環境中,那么如果代碼中出現了重名的函數或者變量,JavaScript 引擎會如何處理?

我們先看下面這樣一段代碼:

function showName() {
    console.log('極客邦');
}
showName();
function showName() {
    console.log('極客時間');
}
showName();

在上面代碼中,我們先定義了一個 showName 的函數,該函數打印出來“極客邦”;然后調用 showName,并定義了一個 showName 函數,這個 showName 函數打印出來的是“極客時間”;最后接著繼續調用 showName。那么你能分析出來這兩次調用打印出來的值是什么嗎?

我們來分析下其完整執行流程:

  • 首先是編譯階段。遇到了第一個 showName 函數,會將該函數體存放到變量環境中。接下來是第二個 showName 函數,繼續存放至變量環境中,但是變量環境中已經存在一個 showName 函數了,此時,第二個 showName 函數會將第一個 showName 函數覆蓋掉。這樣變量環境中就只存在第二個 showName 函數了。

  • 接下來是執行階段。先執行第一個 showName 函數,但由于是從變量環境中查找 showName 函數,而變量環境中只保存了第二個 showName 函數,所以最終調用的是第二個函數,打印的內容是“極客時間”。第二次執行 showName 函數也是走同樣的流程,所以輸出的結果也是“極客時間”。

綜上所述,一段代碼如果定義了兩個相同名字的函數,那么最終生效的是最后一個函數。

關于“javascript變量提升的相關知識有哪些”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

怀集县| 新民市| 鄢陵县| 扬中市| 和田市| 博白县| 文登市| 巨鹿县| 西宁市| 德清县| 甘肃省| 河北省| 井冈山市| 武强县| 海南省| 交城县| 连州市| 贵南县| 九台市| 浦城县| 桂阳县| 竹山县| 灵台县| 灌南县| 合肥市| 厦门市| 孙吴县| 三门县| 深州市| 同心县| 贡嘎县| 阳西县| 罗源县| 体育| 屏东市| 栾城县| 田阳县| 诏安县| 神木县| 紫金县| 富平县|