不要因?yàn)檎嫦嗪茈y理解就不告訴我真相是什么
這篇文章格式不好,請(qǐng)看我整理格式后的: http://www.reibang.com/p/b5a7bb95d8e0
也不知道最開始是在哪個(gè)老師還是哪本書或者是哪個(gè)同學(xué)那里聽來的秀又,或者說學(xué)來的說js是一門不需要編譯的語言,由瀏覽器直接解釋執(zhí)行。嗯很好理解嘛,當(dāng)時(shí)覺得也很對(duì)斧拍,寫完一個(gè)js文件引入到html中直接扔瀏覽器里面跑就OK了,并沒有像Java那樣要做一個(gè)javac hello.java 得到一個(gè)hello.class的編譯步驟杖小。但是當(dāng)我學(xué)到es6的時(shí)候就徹底懵逼了肆汹,es6中的的模塊章節(jié)中通篇都在講述import 和 expert這兩個(gè)風(fēng)騷到飛起的指令的各種花樣解鎖姿勢(shì),各種引用予权,其中很重要很基本的一個(gè)概念就是它跟node中require的區(qū)別昂勉?區(qū)別是什么呢,就是es6中的import是編譯時(shí)加載的扫腺,而node中的require是運(yùn)行時(shí)加載的岗照。還有另一個(gè)說法就是,es6中的import是靜態(tài)編譯階段執(zhí)行的笆环,所以在你import的代碼中不可以寫任何的一絲一毫的js邏輯判斷或者運(yùn)算的代碼攒至。比如阮老師告訴我們 import { 'f' + 'oo' } from 'my_module'; 這樣寫會(huì)報(bào)錯(cuò),原因是import 是靜態(tài)執(zhí)行的而 大括號(hào)中的加法代碼是運(yùn)行時(shí)的操作咧织,所以報(bào)錯(cuò)了嗓袱。
大神們對(duì)于自己理解的知識(shí)說起來總是一筆帶過,從不關(guān)心屌絲們對(duì)于一個(gè)概念為什么是這樣的可能都不懂习绢。
自己就曾在這里鉆過很長時(shí)間的牛角尖渠抹,偏執(zhí)于大家信口拈來的 ‘編譯時(shí)’ 這個(gè)概念到底是發(fā)生在什么時(shí)候? 它都做了什么工作闪萄?
后來在看了一些書籍以及度娘之后終于有所領(lǐng)悟梧却,為防止自己日后再次遺忘,特別記錄在這里败去。
其實(shí)對(duì)于js的編譯期或者說預(yù)編譯放航,我自己是一直都知道的,只是自己沒有把這個(gè) 動(dòng)作 跟j s的編譯聯(lián)系到一塊兒而已圆裕。直接上例子??
劇透: 其實(shí)你想象j s中的hosting(變量提升)就懂了
理解預(yù)編譯首先要弄清楚兩個(gè)概念: 函數(shù)聲明和變量賦值
function xiaoyu() {} // 函數(shù)聲明 這種形式的寫法是函數(shù)聲明广鳍,也即是聲明一個(gè)函數(shù),這種寫法吓妆,腳本在執(zhí)行之前會(huì)做預(yù)編譯處理(這里很重要哦赊时,要記得函數(shù)聲明是會(huì)有一個(gè)高的優(yōu)先級(jí)的先編譯后執(zhí)行)
再來看另一種寫法:
var xiaoyu = function() {} // 變量賦值
這種寫法就屬于是變量的賦值了,函數(shù)在js中也是一種數(shù)據(jù)行拢,匿名函數(shù)作為變量賦值給定義的變量祖秒。這種形式的寫法,在編譯階段也會(huì)做處理,但是但是但是but只會(huì)給變量xiaoyu分配一個(gè)內(nèi)存空間竭缝,不會(huì)初始化(好吧房维,初始化為undefined了)具體值的初始化是在程序執(zhí)行階段。
好了接下來就可以正式看例子了:
??1:
function xiaoyu() {
alert('xiaoyu')
}
xiaoyu()
function xiaoyu() {
alert('xiaoyu2')
}
xiaoyu()
分析這段代碼抬纸,首先判斷兩個(gè)都屬于是函數(shù)聲明咙俩,都會(huì)在預(yù)編譯階段處理,而函數(shù)名相同松却,后面聲明的會(huì)覆蓋前面的在執(zhí)行階段就只會(huì)看到后面的暴浦。所以將代碼復(fù)制到控制臺(tái)執(zhí)行會(huì)看到兩個(gè)xiaoyu2
??2:
var xiaoyu = function () {alert('xiaoyu')}
xiaoyu()
xiaoyu = function(){alert('xiaoyu2')}
xiaoyu()
分析代碼,首先判斷兩個(gè)都屬于是變量賦值晓锻,而且兩個(gè)變量名一樣歌焦,所以在編譯階段只會(huì)分配一個(gè)內(nèi)存空間存放變量xiaoyu的內(nèi)容,當(dāng)代碼執(zhí)行時(shí)候按照順序執(zhí)行和賦值砚哆,會(huì)先后彈出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()
分析代碼独撇,首先第一個(gè)屬于函數(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ù)聲明和變量聲明會(huì)在預(yù)編譯階段被’提升‘并且變量的提升是被最優(yōu)先的提升的,也就是說如果一個(gè)函數(shù)聲明一個(gè)和一個(gè)變量同名了比如例子三中的那么變量名會(huì)被優(yōu)先提升到最高 var xiaoyu // 不賦值 為undefined 然后提升函數(shù)聲明 function xiaoyu() {alert('xiaoyu')} // 此時(shí)因?yàn)楹瘮?shù)聲明在后所以它覆蓋了xiaoyu變量給它復(fù)制為一個(gè)函數(shù)战转。當(dāng)js引擎做完所有的這些提升的工作后js才會(huì)按照代碼順序來執(zhí)行搜立。
另外需要注意的是 js不是全文編譯完成再執(zhí)行,而是塊編譯槐秧,即一個(gè)script塊中預(yù)編譯然后執(zhí)行啄踊,再按順序預(yù)編譯下一個(gè)script塊再執(zhí)行 但是此時(shí)上一個(gè)script快中的數(shù)據(jù)都是可用的了,而下一個(gè)塊中的函數(shù)和變量則是不可用的刁标。
OK 講到這里颠通,是不是有點(diǎn)懂得es6中大編譯時(shí)執(zhí)行是什么意思了,它就是在代碼執(zhí)行前做的一個(gè)預(yù)編譯也可以叫做靜態(tài)編譯膀懈,好處是讓你可以在執(zhí)行任何代碼前預(yù)初始化更多的模塊結(jié)構(gòu)顿锰,這樣如果引用尚未賦值的export,能得到更好的錯(cuò)誤信息启搂。例如硼控,一個(gè)let綁定會(huì)扔出異常——如果你在它被賦值之前就引用它的話——你可以得到清晰的錯(cuò)誤信息胳赌。而一個(gè)動(dòng)態(tài)模塊對(duì)象上的屬性如果還未賦值就被引用牢撼,得到的是undefined,最終錯(cuò)誤可能發(fā)生在客戶代碼中匈织,必須跟蹤這個(gè)錯(cuò)誤直到源頭——這比異常要難調(diào)試太多了。
累了,先寫到這里