Javascript變量函數聲明提升深刻理解
前言:
Javascript變量函數聲明提升(Hoisting)是在 Javascript 中執行上下文工作方式的一種認識(也可以說是一種預編譯),從字面意義上看,“變量提升”意味著變量和函數的聲明會在物理層面移動到代碼的最前面,在代碼裡的位置是不會動的,而是在編譯階段被放入內存中會和代碼順序不一樣。變量函數聲明提升雖然對於實際編碼影響不大,特別是現在ES6的普及,但作為前端算是一個基礎知識,必須掌握的,是很多大廠的前端面試必問的知識點之一。在這裡分享,不是什麼新鮮的內容,隻是作為一個自己的學習筆記,加速對其的理解。
變量知道是ES5中的 var 和 function 中的產物,ES6中的 let 、 const 則不存在有變量提升。
變量提升
JavaScript引擎的工作方式是先解析代碼,獲取所有聲明的變量和函數,然後再一行一行地運行。這造成的結果,就是所有的變量的聲明語句,都會被提升到代碼的頭部,這就叫做變量提升(Hoisting)。
這裡說的變量聲明,包括函數的聲明,接下來看看代碼:
function hoistingVariable() { if (!devpoint) { var devpoint = 1; } console.log(devpoint); } hoistingVariable(); // 下面是輸出結果 // 1
變量所處的作用域為函數體內,解析的時候查找該作用域中的聲明的變量,devpoint
在if雖然未聲明,根據變量提升規則,變量的聲明提升到函數的第一行,但未賦值。
實際的效果等同於下面的代碼:
function hoistingVariable() { var devpoint; if (!devpoint) { devpoint = 1; } console.log(devpoint); } hoistingVariable();
接下再增加一些迷惑的代碼,如下:
var devpoint = "out"; function hoistingVariable() { var devpoint; if (!devpoint) { devpoint = "in"; } console.log(devpoint); } hoistingVariable(); console.log(devpoint); // 下面是輸出結果 // in // out
對於同名變量聲明,個人理解是先找作用域,就近原則,函數體內聲明(前提是有聲明 var ),就隻找函數內查找,並不受函數外聲明的影響。
把上面函數體內的聲明語句去掉,輸出情況也就不一樣。
var devpoint = "out"; function hoistingVariable() { if (!devpoint) { devpoint = "in"; } console.log(devpoint); } hoistingVariable(); console.log(devpoint); // 下面是輸出結果 // out // out
函數體內聲明語句去掉後,這是就需要去函數體外找聲明,根據這一條,函數外聲明並賦值瞭,函數體內的 if 語句就不會執行。
下面代碼調整瞭賦值的順序,代碼如下:
var devpoint; function hoistingVariable() { if (!devpoint) { devpoint = "in"; } console.log(devpoint); } devpoint = "out"; hoistingVariable(); console.log(devpoint); // 下面是輸出結果 // out // out
根據上面說的,函數體內的變量是外部聲明的,但未賦值,函數是提升瞭,並為執行。在函數執行前賦值給devpoint
,再執行就變成瞭out
。
函數提升
上面介紹過,變量提升,同樣包括函數的聲明,不同方式的函數聲明,執行也有所不同。這種問題就是直接上代碼。
function hoistingFun() { hello(); function hello() { console.log("hello"); } } hoistingFun(); // 下面是輸出結果 // hello
上面的代碼能夠正常運行是因為函數聲明被提升,函數 hello
被提升到頂部,運行效果跟下面代碼一致:
function hoistingFun() { function hello() { console.log("hello"); } hello(); } hoistingFun(); // 下面是輸出結果 // hello
如果在同一個作用域中對同一個函數進行聲明,後面的函數會覆蓋前面的函數聲明。
function hoistingFun() { hello(); function hello() { console.log("hello"); } function hello() { console.log("hello2"); } } hoistingFun(); // 下面是輸出結果 // hello2
兩個函數聲明都被提升瞭,按照聲明的順序,後面的聲明覆蓋前面的聲明。
函數聲明常見的方式有兩種,還有一種是匿名函數表達式聲明方式,這種方式可以視為是變量的聲明來處理,當作用域中有函數聲明和變量聲明時,函數聲明的優先級最高,將上面的代碼更改後,結果就不一樣瞭,
如下:
function hoistingFun() { hello(); function hello() { console.log("hello"); } var hello = function () { console.log("hello2"); }; } hoistingFun(); // 下面是輸出結果 // hello
上面的代碼,編譯邏輯如下:
function hoistingFun() { function hello() { console.log("hello"); } hello(); hello = function () { console.log("hello2"); }; } hoistingFun(); // 下面是輸出結果 // hello
接下來再來看下,外部使用變量聲明,函數體內使用函數聲明的示例:
var hello = 520; function hoistingFun() { console.log(hello); hello = 521; console.log(hello); function hello() { console.log("hello"); } } hoistingFun(); console.log(hello); // 下面是輸出結果 // [Function: hello] // 521 // 520
上面說過,在函數體內聲明過的變量或者函數,隻作用於函數體內,受限於函數體內,不受外部聲明的影響,相當於函數體內作用域與外部隔離。
上面代碼的編譯後的邏輯如下:
var hello = 520; function hoistingFun() { function hello() { console.log("hello"); } console.log(hello); hello = 521; console.log(hello); } hoistingFun(); console.log(hello);
在變量聲明中,函數的優先權最高,永遠提升到作用域最頂部,然後才是函數表達式和變量的執行順序。
來看下面的代碼:
var hello = 520; function hello() { console.log("hello"); } console.log(hello); // 下面是輸出結果 // 520
根據函數聲明優先級最高的原則,上面代碼的執行邏輯如下:
function hello() { console.log("hello"); } hello = 520; console.log(hello);
為什麼要提升?
至於為什麼要提升,這裡不做詳細介紹,提供一些參考文章,有興趣的可以去查閱
- 《 Note 4. Two words about “hoisting”》
- 《淺析JavaScript中的變量提升》
最佳實踐
現代Javascript中,已經有很多方式避免變量提升帶來的問題,使用let
、const
替代var
,使用eslint
等工具避免變量重復定義,在一些前端開發團隊中,可以針對團隊做一些規范化的腳手架,如項目初始化強制項目的目錄、eslint的最佳配置等,用程序規范的過程比人督促要靠譜。
下面的代碼可以看到const 和 var 聲明的變量的區別,const 聲明的變量不會提升,具體的區別可以查閱《javascript 變量聲明 var,let,const 的區別》
console.log("1a", myTitle1); if (1) { console.log("1b", myTitle1); var myTitle1 = "devpoint"; } if (1) { // 這裡的代碼是有錯誤無法執行 console.log("3c", myTitle2); const myTitle2 = "devpoint"; } // 下面是輸出結果 // 1a undefined // 1b undefined
總結
通過自我學習變量函數提升,加深瞭對其理解,對於前端面試所涉及的類似問題可以自信的給出答案,算是一種收獲。
到此這篇關於Javascript變量函數聲明提升加深理解的文章就介紹到這瞭,更多相關JS變量聲明內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!