本篇文章為大家帶來了關於javascript變數提升的相關知識,什麼是變數提升,為什麼會有變數提升,下面一起來看吧,希望對大家有幫助。
相關推薦:javascript學習教學
#咱們先看段落程式碼,你覺得下面這段程式碼輸出的結果是什麼?
showName() console.log(myname) var myname = '极客时间' function showName() { console.log('函数showName被执行'); }
使用過 JavaScript 開發的程式設計師應該都知道,JavaScript 是依照順序執行的。若按照這個邏輯來理解的話,那麼:
然而實際執行結果卻並非如此, 如下圖:
第1 行輸出“函數showName 被執行”,第2 行輸出“undefined”,這和前面想像中的順序執行有點不一樣!
透過上面的執行結果,你應該已經知道函數或變數可以在定義之前使用,那麼如果使用沒有定義的變數或函數,JavaScript 程式碼還能繼續執行了嗎?為了驗證這一點,我們可以刪除第3 行變數myname 的定義,如下所示:
showName() console.log(myname) function showName() { console.log('函数showName被执行'); }
然後再次執行這段程式碼時,JavaScript 引擎就會報錯,結果如下:
從上面兩段程式碼的執行結果來看,我們可以得到如下三個結論:
第一個結論很好理解,因為變數沒有定義,這樣在執行 JavaScript 程式碼時,就找不到該變數,所以 JavaScript 會拋出錯誤。
但是對於第二個和第三個結論,就挺讓人費解的:
要解釋這兩個問題,你就需要先了解下什麼是變數提升。
不過在介紹變數提升之前,我們先透過下面這段程式碼,來看看什麼是 JavaScript 中的宣告和賦值。
var myname = '极客时间'
這段程式碼你可以把它看成是兩行程式碼組成的:
var myname //声明部分 myname = '极客时间' //赋值部分
如下圖所示:
上面是變數的宣告和賦值,那接下來我們再來看看函數的宣告和賦值,結合下面這段程式碼:
function foo(){ console.log('foo') } var bar = function(){ console.log('bar') }
第一個函數foo 是一個完整的函數聲明,也就是說沒有涉及到賦值運算;第二個函數是先宣告變數bar,再把function(){console.log('bar')}賦值給bar。為了直覺理解,你可以參考下圖:
好了,了解宣告和賦值運算,那接下來我們就可以聊聊什麼是變數提升了。
所謂的變數提升,是指在 JavaScript 程式碼執行過程中,JavaScript 引擎會將變數的宣告部分和函數的宣告部分提升到程式碼開頭的「行為」。變數被提升後,會為變數設定預設值,這個預設值就是我們熟悉的 undefined。
我們來模擬下實作:
/* * 变量提升部分 */// 把变量 myname提升到开头,// 同时给myname赋值为undefinedvar myname = undefined// 把函数showName提升到开头function showName() { console.log('showName被调用');}/* * 可执行代码部分 */showName()console.log(myname)// 去掉var声明部分,保留赋值语句myname = '极客时间'
為了模擬變數提升的效果,我們對程式碼做了以下調整,如下圖:
從圖中可以看出,原來的程式碼主要做了兩處調整:
通过这两步,就可以实现变量提升的效果。你也可以执行这段模拟变量提升的代码,其输出结果和第一段代码应该是完全一样的。
通过这段模拟的变量提升代码,相信你已经明白了可以在定义之前使用变量或者函数的原因——函数和变量在执行之前都提升到了代码开头。
从概念的字面意义上来看,“变量提升”意味着变量和函数的声明会在物理层面移动到代码的最前面,正如我们所模拟的那样。但,这并不准确。实际上变量和函数声明在代码里的位置是不会改变的,而且是在编译阶段被 JavaScript 引擎放入内存中。对,你没听错,一段 JavaScript 代码在执行之前需要被 JavaScript 引擎编译,编译完成之后,才会进入执行阶段。大致流程你可以参考下图:
那么编译阶段和变量提升存在什么关系呢?
为了搞清楚这个问题,我们还是回过头来看上面那段模拟变量提升的代码,为了方便介绍,可以把这段代码分成两部分。
第一部分:变量提升部分的代码。
var myname = undefined function showName() { console.log('函数showName被执行'); }
第二部分:执行部分的代码。
showName() console.log(myname) myname = '极客时间'
下面我们就可以把 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被执行'); }
我们可以一行一行来分析上述代码:
这样就生成了变量环境对象。接下来 JavaScript 引擎会把声明以外的代码编译为字节码,至于字节码的细节,我也会在后面文章中做详细介绍,你可以类比如下的模拟代码:
showName() console.log(myname) myname = '极客时间'
好了,现在有了执行上下文和可执行代码了,那么接下来就到了执行阶段了。
JavaScript 引擎开始执行“可执行代码”,按照顺序一行一行地执行。下面我们就来一行一行分析下这个执行过程:
VariableEnvironment: myname -> "极客时间", showName ->function : {console.log(myname)
好了,以上就是一段代码的编译和执行流程 。
现在你已经知道了,在执行一段 JavaScript 代码之前,会编译代码,并将代码中的函数和变量保存到执行上下文的变量环境中,那么如果代码中出现了重名的函数或者变量,JavaScript 引擎会如何处理?
我们先看下面这样一段代码:
function showName() { console.log('极客邦'); } showName(); function showName() { console.log('极客时间'); } showName();
在上面代码中,我们先定义了一个 showName 的函数,该函数打印出来“极客邦”;然后调用 showName,并定义了一个 showName 函数,这个 showName 函数打印出来的是“极客时间”;最后接着继续调用 showName。那么你能分析出来这两次调用打印出来的值是什么吗?
我们来分析下其完整执行流程:
综上所述,一段代码如果定义了两个相同名字的函数,那么最终生效的是最后一个函数。
好了,今天就到这里,下面我来简单总结下今天的主要内容:
相关推荐:javascript学习教程
以上是JavaScript的執行機制-變數提升(實例詳解)的詳細內容。更多資訊請關注PHP中文網其他相關文章!