理解JavaScript的預(yù)編譯

不要因?yàn)檎嫦嗪茈y理解就不告訴我真相是什么
這篇文章格式不好,請看我整理格式后的: http://www.itdecent.cn/p/b5a7bb95d8e0

也不知道最開始是在哪個老師還是哪本書或者是哪個同學(xué)那里聽來的,或者說學(xué)來的說js是一門不需要編譯的語言,由瀏覽器直接解釋執(zhí)行。嗯很好理解嘛,當(dāng)時覺得也很對,寫完一個js文件引入到html中直接扔瀏覽器里面跑就OK了,并沒有像Java那樣要做一個javac hello.java 得到一個hello.class的編譯步驟。但是當(dāng)我學(xué)到es6的時候就徹底懵逼了,es6中的的模塊章節(jié)中通篇都在講述import 和 expert這兩個風(fēng)騷到飛起的指令的各種花樣解鎖姿勢,各種引用,其中很重要很基本的一個概念就是它跟node中require的區(qū)別?區(qū)別是什么呢,就是es6中的import是編譯時加載的,而node中的require是運(yùn)行時加載的。還有另一個說法就是,es6中的import是靜態(tài)編譯階段執(zhí)行的,所以在你import的代碼中不可以寫任何的一絲一毫的js邏輯判斷或者運(yùn)算的代碼。比如阮老師告訴我們 import { 'f' + 'oo' } from 'my_module'; 這樣寫會報(bào)錯,原因是import 是靜態(tài)執(zhí)行的而 大括號中的加法代碼是運(yùn)行時的操作,所以報(bào)錯了。
大神們對于自己理解的知識說起來總是一筆帶過,從不關(guān)心屌絲們對于一個概念為什么是這樣的可能都不懂。
自己就曾在這里鉆過很長時間的牛角尖,偏執(zhí)于大家信口拈來的 ‘編譯時’ 這個概念到底是發(fā)生在什么時候? 它都做了什么工作?
后來在看了一些書籍以及度娘之后終于有所領(lǐng)悟,為防止自己日后再次遺忘,特別記錄在這里。

其實(shí)對于js的編譯期或者說預(yù)編譯,我自己是一直都知道的,只是自己沒有把這個 動作 跟j s的編譯聯(lián)系到一塊兒而已。直接上例子??

劇透: 其實(shí)你想象j s中的hosting(變量提升)就懂了
理解預(yù)編譯首先要弄清楚兩個概念: 函數(shù)聲明和變量賦值
function xiaoyu() {} // 函數(shù)聲明 這種形式的寫法是函數(shù)聲明,也即是聲明一個函數(shù),這種寫法,腳本在執(zhí)行之前會做預(yù)編譯處理(這里很重要哦,要記得函數(shù)聲明是會有一個高的優(yōu)先級的先編譯后執(zhí)行)
再來看另一種寫法:
var xiaoyu = function() {} // 變量賦值
這種寫法就屬于是變量的賦值了,函數(shù)在js中也是一種數(shù)據(jù),匿名函數(shù)作為變量賦值給定義的變量。這種形式的寫法,在編譯階段也會做處理,但是但是但是but只會給變量xiaoyu分配一個內(nèi)存空間,不會初始化(好吧,初始化為undefined了)具體值的初始化是在程序執(zhí)行階段。
好了接下來就可以正式看例子了:
??1:
function xiaoyu() {
alert('xiaoyu')
}
xiaoyu()
function xiaoyu() {
alert('xiaoyu2')
}
xiaoyu()
分析這段代碼,首先判斷兩個都屬于是函數(shù)聲明,都會在預(yù)編譯階段處理,而函數(shù)名相同,后面聲明的會覆蓋前面的在執(zhí)行階段就只會看到后面的。所以將代碼復(fù)制到控制臺執(zhí)行會看到兩個xiaoyu2
??2:
var xiaoyu = function () {alert('xiaoyu')}
xiaoyu()
xiaoyu = function(){alert('xiaoyu2')}
xiaoyu()
分析代碼,首先判斷兩個都屬于是變量賦值,而且兩個變量名一樣,所以在編譯階段只會分配一個內(nèi)存空間存放變量xiaoyu的內(nèi)容,當(dāng)代碼執(zhí)行時候按照順序執(zhí)行和賦值,會先后彈出xiaoyu 和xiaoyu2
var xiaoyu // undefined
xiaoyu = function () {alert('xiaoyu')}
xiaoyu() // 'xiaoyu'
xiaoyu = function(){alert('xiaoyu2')}
xiaoyu() // 'xiaoyu2'
明白了吧
??3:
function xiaoyu() {alert('xiaoyu')}
xiaoyu()
xiaoyu = function() {alert('xiaoyu2')}
xiaoyu()
分析代碼,首先第一個屬于函數(shù)聲明,后一種屬于變量賦值。所以預(yù)編譯處理完后代碼應(yīng)該是這樣的:
var xiaoyu // undefined
function xiaoyu() {alert('xiaoyu')}
xiaoyu() // 'xiaoyu'
xiaoyu = function() {alert('xiaoyu2')}
xiaoyu() // 'xiaoyu2'
??4:

window.alert(xiaoyu)
function xiaoyu() {}
window.alert(xiaoyu)
var xiaoyu = 123
以上代碼編譯結(jié)束應(yīng)該是這樣的:
var xiaoyu // undefined
function xiaoyu () {alert('xiaoyu')}
window.alert(xiaoyu) // function xiaoyu() {}
window.alert(xiaoyu) // function xiaoyu() {}
xiaoyu = 123

總結(jié)可以得出,函數(shù)聲明和變量聲明會在預(yù)編譯階段被’提升‘并且變量的提升是被最優(yōu)先的提升的,也就是說如果一個函數(shù)聲明一個和一個變量同名了比如例子三中的那么變量名會被優(yōu)先提升到最高 var xiaoyu // 不賦值 為undefined 然后提升函數(shù)聲明 function xiaoyu() {alert('xiaoyu')} // 此時因?yàn)楹瘮?shù)聲明在后所以它覆蓋了xiaoyu變量給它復(fù)制為一個函數(shù)。當(dāng)js引擎做完所有的這些提升的工作后js才會按照代碼順序來執(zhí)行。

另外需要注意的是 js不是全文編譯完成再執(zhí)行,而是塊編譯,即一個script塊中預(yù)編譯然后執(zhí)行,再按順序預(yù)編譯下一個script塊再執(zhí)行 但是此時上一個script快中的數(shù)據(jù)都是可用的了,而下一個塊中的函數(shù)和變量則是不可用的。

OK 講到這里,是不是有點(diǎn)懂得es6中大編譯時執(zhí)行是什么意思了,它就是在代碼執(zhí)行前做的一個預(yù)編譯也可以叫做靜態(tài)編譯,好處是讓你可以在執(zhí)行任何代碼前預(yù)初始化更多的模塊結(jié)構(gòu),這樣如果引用尚未賦值的export,能得到更好的錯誤信息。例如,一個let綁定會扔出異常——如果你在它被賦值之前就引用它的話——你可以得到清晰的錯誤信息。而一個動態(tài)模塊對象上的屬性如果還未賦值就被引用,得到的是undefined,最終錯誤可能發(fā)生在客戶代碼中,必須跟蹤這個錯誤直到源頭——這比異常要難調(diào)試太多了。

累了,先寫到這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容