Javascript變量函數(shù)聲明提升深刻理解
前言:
Javascript變量函數(shù)聲明提升(Hoisting)是在 Javascript 中執(zhí)行上下文工作方式的一種認(rèn)識(shí)(也可以說(shuō)是一種預(yù)編譯),從字面意義上看,“變量提升”意味著變量和函數(shù)的聲明會(huì)在物理層面移動(dòng)到代碼的最前面,在代碼里的位置是不會(huì)動(dòng)的,而是在編譯階段被放入內(nèi)存中會(huì)和代碼順序不一樣。變量函數(shù)聲明提升雖然對(duì)于實(shí)際編碼影響不大,特別是現(xiàn)在ES6的普及,但作為前端算是一個(gè)基礎(chǔ)知識(shí),必須掌握的,是很多大廠的前端面試必問(wèn)的知識(shí)點(diǎn)之一。在這里分享,不是什么新鮮的內(nèi)容,只是作為一個(gè)自己的學(xué)習(xí)筆記,加速對(duì)其的理解。
變量知道是ES5中的 var 和 function 中的產(chǎn)物,ES6中的 let 、 const 則不存在有變量提升。
變量提升
JavaScript引擎的工作方式是先解析代碼,獲取所有聲明的變量和函數(shù),然后再一行一行地運(yùn)行。這造成的結(jié)果,就是所有的變量的聲明語(yǔ)句,都會(huì)被提升到代碼的頭部,這就叫做變量提升(Hoisting)。
這里說(shuō)的變量聲明,包括函數(shù)的聲明,接下來(lái)看看代碼:
function hoistingVariable() { if (!devpoint) { var devpoint = 1; } console.log(devpoint); } hoistingVariable(); // 下面是輸出結(jié)果 // 1
變量所處的作用域?yàn)楹瘮?shù)體內(nèi),解析的時(shí)候查找該作用域中的聲明的變量,devpoint
在if雖然未聲明,根據(jù)變量提升規(guī)則,變量的聲明提升到函數(shù)的第一行,但未賦值。
實(shí)際的效果等同于下面的代碼:
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); // 下面是輸出結(jié)果 // in // out
對(duì)于同名變量聲明,個(gè)人理解是先找作用域,就近原則,函數(shù)體內(nèi)聲明(前提是有聲明 var ),就只找函數(shù)內(nèi)查找,并不受函數(shù)外聲明的影響。
把上面函數(shù)體內(nèi)的聲明語(yǔ)句去掉,輸出情況也就不一樣。
var devpoint = "out"; function hoistingVariable() { if (!devpoint) { devpoint = "in"; } console.log(devpoint); } hoistingVariable(); console.log(devpoint); // 下面是輸出結(jié)果 // out // out
函數(shù)體內(nèi)聲明語(yǔ)句去掉后,這是就需要去函數(shù)體外找聲明,根據(jù)這一條,函數(shù)外聲明并賦值了,函數(shù)體內(nèi)的 if 語(yǔ)句就不會(huì)執(zhí)行。
下面代碼調(diào)整了賦值的順序,代碼如下:
var devpoint; function hoistingVariable() { if (!devpoint) { devpoint = "in"; } console.log(devpoint); } devpoint = "out"; hoistingVariable(); console.log(devpoint); // 下面是輸出結(jié)果 // out // out
根據(jù)上面說(shuō)的,函數(shù)體內(nèi)的變量是外部聲明的,但未賦值,函數(shù)是提升了,并為執(zhí)行。在函數(shù)執(zhí)行前賦值給devpoint
,再執(zhí)行就變成了out
。
函數(shù)提升
上面介紹過(guò),變量提升,同樣包括函數(shù)的聲明,不同方式的函數(shù)聲明,執(zhí)行也有所不同。這種問(wèn)題就是直接上代碼。
function hoistingFun() { hello(); function hello() { console.log("hello"); } } hoistingFun(); // 下面是輸出結(jié)果 // hello
上面的代碼能夠正常運(yùn)行是因?yàn)楹瘮?shù)聲明被提升,函數(shù) hello
被提升到頂部,運(yùn)行效果跟下面代碼一致:
function hoistingFun() { function hello() { console.log("hello"); } hello(); } hoistingFun(); // 下面是輸出結(jié)果 // hello
如果在同一個(gè)作用域中對(duì)同一個(gè)函數(shù)進(jìn)行聲明,后面的函數(shù)會(huì)覆蓋前面的函數(shù)聲明。
function hoistingFun() { hello(); function hello() { console.log("hello"); } function hello() { console.log("hello2"); } } hoistingFun(); // 下面是輸出結(jié)果 // hello2
兩個(gè)函數(shù)聲明都被提升了,按照聲明的順序,后面的聲明覆蓋前面的聲明。
函數(shù)聲明常見(jiàn)的方式有兩種,還有一種是匿名函數(shù)表達(dá)式聲明方式,這種方式可以視為是變量的聲明來(lái)處理,當(dāng)作用域中有函數(shù)聲明和變量聲明時(shí),函數(shù)聲明的優(yōu)先級(jí)最高,將上面的代碼更改后,結(jié)果就不一樣了,
如下:
function hoistingFun() { hello(); function hello() { console.log("hello"); } var hello = function () { console.log("hello2"); }; } hoistingFun(); // 下面是輸出結(jié)果 // hello
上面的代碼,編譯邏輯如下:
function hoistingFun() { function hello() { console.log("hello"); } hello(); hello = function () { console.log("hello2"); }; } hoistingFun(); // 下面是輸出結(jié)果 // hello
接下來(lái)再來(lái)看下,外部使用變量聲明,函數(shù)體內(nèi)使用函數(shù)聲明的示例:
var hello = 520; function hoistingFun() { console.log(hello); hello = 521; console.log(hello); function hello() { console.log("hello"); } } hoistingFun(); console.log(hello); // 下面是輸出結(jié)果 // [Function: hello] // 521 // 520
上面說(shuō)過(guò),在函數(shù)體內(nèi)聲明過(guò)的變量或者函數(shù),只作用于函數(shù)體內(nèi),受限于函數(shù)體內(nèi),不受外部聲明的影響,相當(dāng)于函數(shù)體內(nèi)作用域與外部隔離。
上面代碼的編譯后的邏輯如下:
var hello = 520; function hoistingFun() { function hello() { console.log("hello"); } console.log(hello); hello = 521; console.log(hello); } hoistingFun(); console.log(hello);
在變量聲明中,函數(shù)的優(yōu)先權(quán)最高,永遠(yuǎn)提升到作用域最頂部,然后才是函數(shù)表達(dá)式和變量的執(zhí)行順序。
來(lái)看下面的代碼:
var hello = 520; function hello() { console.log("hello"); } console.log(hello); // 下面是輸出結(jié)果 // 520
根據(jù)函數(shù)聲明優(yōu)先級(jí)最高的原則,上面代碼的執(zhí)行邏輯如下:
function hello() { console.log("hello"); } hello = 520; console.log(hello);
為什么要提升?
至于為什么要提升,這里不做詳細(xì)介紹,提供一些參考文章,有興趣的可以去查閱
最佳實(shí)踐
現(xiàn)代Javascript中,已經(jīng)有很多方式避免變量提升帶來(lái)的問(wèn)題,使用let
、const
替代var
,使用eslint
等工具避免變量重復(fù)定義,在一些前端開(kāi)發(fā)團(tuán)隊(duì)中,可以針對(duì)團(tuán)隊(duì)做一些規(guī)范化的腳手架,如項(xiàng)目初始化強(qiáng)制項(xiàng)目的目錄、eslint的最佳配置等,用程序規(guī)范的過(guò)程比人督促要靠譜。
下面的代碼可以看到const 和 var 聲明的變量的區(qū)別,const 聲明的變量不會(huì)提升,具體的區(qū)別可以查閱《javascript 變量聲明 var,let,const 的區(qū)別》
console.log("1a", myTitle1); if (1) { console.log("1b", myTitle1); var myTitle1 = "devpoint"; } if (1) { // 這里的代碼是有錯(cuò)誤無(wú)法執(zhí)行 console.log("3c", myTitle2); const myTitle2 = "devpoint"; } // 下面是輸出結(jié)果 // 1a undefined // 1b undefined
總結(jié)
通過(guò)自我學(xué)習(xí)變量函數(shù)提升,加深了對(duì)其理解,對(duì)于前端面試所涉及的類似問(wèn)題可以自信的給出答案,算是一種收獲。
到此這篇關(guān)于Javascript變量函數(shù)聲明提升加深理解的文章就介紹到這了,更多相關(guān)JS變量聲明內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS實(shí)現(xiàn)換膚功能的方法實(shí)例詳解
這篇文章主要介紹了JS實(shí)現(xiàn)換膚功能的方法,結(jié)合實(shí)例形式分析了javascript針對(duì)頁(yè)面元素屬性與樣式動(dòng)態(tài)操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-01-01javascript 無(wú)提示關(guān)閉窗口腳本
在IE7、IE8中,使用JavaScript提供的close()方法都可以關(guān)閉當(dāng)前窗口或標(biāo)簽,但都提示討厭的對(duì)話框,找了下代碼,終于可以無(wú)提示直接關(guān)閉了。2009-08-08微信小程序-橫向滑動(dòng)scroll-view隱藏滾動(dòng)條
本篇文章主要介紹了微信小程序-橫向滑動(dòng)scroll-view隱藏滾動(dòng)條的相關(guān)知識(shí)。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-04-04Javascript中的window.event.keyCode使用介紹
我們之前發(fā)過(guò)不少關(guān)于event.keyCode相關(guān)的文章,大家都可以參考下。2011-04-04JS+canvas動(dòng)態(tài)繪制餅圖的方法示例
這篇文章主要介紹了JS+canvas動(dòng)態(tài)繪制餅圖的方法,結(jié)合具體實(shí)例形式分析了js+canvas實(shí)現(xiàn)餅狀圖形繪制的相關(guān)操作技巧,需要的朋友可以參考下2017-09-09JavaScript中的子窗口與父窗口的互相調(diào)用問(wèn)題
本文給大家介紹了JavaScript中的子窗口與父窗口的互相調(diào)用問(wèn)題,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-02-02javascript 上下banner替換具體實(shí)現(xiàn)
這篇文章介紹了javascript 上下banner替換具體實(shí)現(xiàn),有需要的朋友可以參考一下2013-11-11JavaScript實(shí)現(xiàn)的背景自動(dòng)變色代碼
這篇文章主要介紹了JavaScript實(shí)現(xiàn)的背景自動(dòng)變色代碼,涉及JavaScript數(shù)組操作結(jié)合定時(shí)函數(shù)實(shí)現(xiàn)修改頁(yè)面元素樣式的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10