❝點贊再看,年薪百萬 本文已收錄至https://github.com/likekk/-Blog歡迎你們star😘😘😘,共同進步。若是文章有出現錯誤的地方,歡迎你們指出。後期將在將GitHub上規劃前端學習的路線和資源分享。前端
❞
距離寫上一篇文章已通過去兩個月了(上一篇文章是2020年07月17日寫的),託更有點嚴重,一方面是這幾個月項目在趕,另外一方面是本身近兩個月以來變懶了(爲本身不想更新文章找藉口)😅😅😅,國慶假期,公司放八天假。git
本身沒有去回家,主要是回家有點遠,歷來這邊讀書到如今的國慶期間一直都沒有回過家,這七八天原本也打算出去玩的(確定須要安排時間出去玩),可是想一想仍是學點東西比較好。因而有了這篇博客。github
寫預編譯(執行期上下文)這篇博客的初衷是由於一方面方便本身複習,另外一方面是爲了後續出閉包、做用域和做用域鏈、this指向等等的博客作一些前期的準備,以上幾個知識點能夠說是在基礎中比較難以掌握的,也有可能會在一些面試中常常會問到的。web
首先在講解預編譯以前,咱們先說下JavaScript的語言特色吧!說兩點比較重要的(比較優秀的兩點)面試
咱們知道JavaScript語言首先是單線程的,其次是解釋性語言。對於這兩點我相信你們都不陌生,接着咱們拓展開來,對於解釋執行只是發生在執行的最後一步,解釋執行以前還有兩步,簡單介紹一下JavaScript運行三部曲瀏覽器
「語法分析:」閉包
對於語法分析的話,大概過一下就能夠了,好比:是否少寫個括號,是否有中文等等一系列的問題。JavaScript解釋器會全篇掃描一下,檢查是否有錯誤,可是不會執行。編輯器
「預編譯:」函數
這個就比較有意思了,也是本篇博客的重點,預編譯的話主要分兩種吧!一種是函數體裏面的預編譯(AO),另外一種是全局環境的預編譯(GO),不懂?沒有關係,我會慢慢講到。學習
「解釋執行:」
對於解釋執行執行的話,我想我也能夠不用多說吧!就是在執行的時候解釋一行執行一行唄!
對於預編譯這個概念其實咱們有遇到過,只是咱們不知道它的專業名詞叫作預編譯
納尼?不信我,好吧!咱們看下是否遇到過,先簡單舉個例子來證實一下個人觀點
test();
function test(){
console.log('a');
}
console.log(a);
var a=123;
提問:輸出什麼?
❝答案: a 和 undefined
❞
相信你們均可以作出來,其實這裏面就已經包含預編譯的階段,最開始講的時候就說了,預編譯發生在函數執行的前一刻,因此在函數調用的時候就已經有預編譯這一個階段了。
好的,咱們再來看下另一個示例
test();
function test(){
console.log('a');
}
test();
console.log(a);
var a=123;
❝答案: a 、 a 和 undefined
❞
對於如此簡單的兩道題目,相信在座的各位沒有作不出來的吧!
路人甲:「楊戩哥,這兩道題目好簡單(Low)呀!」
「楊戩」:」是,是很簡單,可是大家知道是什麼原理嗎?爲何第一題中輸出a和undefined,對於a的話相信你們都知道,可是undefined輸出是爲何?你們是否有考慮過。「
路人乙:」楊戩哥,咱們老師講過兩句比較有用的話「
❝函數聲明,總體提高
變量 聲明提高
❞
路人丙:」這兩句話能夠解決不少問題,我在碰到一些問題的時候,就是套用這兩句話。「
楊戩:」路人丙弟弟,你的這兩句話確實能夠解決不少問題,可是有些問題靠這兩句話是解決不了的「
路人丙:」楊戩哥,我不信「
「楊戩」:」好吧!,既然你不信,那我就出道題考考你。「
路人丙:」come on「
function foo(a){
console.log(a);
var a=123;
console.log(a);
function a(){}
console.log(a);
var b=function(){}
console.log(b);
function d(){}
}
foo(1);
楊戩:」提問console.log()都輸出是什麼?「
路人丙:」這、這、這,還有這操做「
路人丙:」算了,我仍是老老實實聽楊戩講吧!,不裝逼了「
我先公佈如下答案吧!
❝答案:function a(){}、12三、12三、function(){}
❞
可是到這裏我仍是沒有那麼快講解預編譯,考慮了一下,講解以前仍是須要鋪墊一點東西,不然很難講清楚。
預編譯前期主要講解兩個東西
❝一、imply global 暗示全局變量,即任何變量,若是變量未經聲明就賦值,此變量就爲全局對象全部
二、一切聲明的全局變量都是window屬性
❞
例一
var a=123;
console.log(a);
function test(){
var a=b=123;
}
test();
console.log(window.a);
console.log(window.b);
依次輸出undefined、123
看第一條,即任何變量,若是變量未經聲明就賦值,此變量就爲全局對象全部
a變量咱們是已經聲明瞭,可是b變量咱們並無聲明,此時就進行賦值,因此歸全局對象全部
例二
var b=123;
console.log(b);
console.log(window.b);
此時b===window.b
第二條,一切聲明的全局變量都是window屬性
講解的不是那麼透徹,等講徹底局預編譯的時候再回過頭來看,你就會有一種醍醐灌頂的感受
預編譯在這裏我主要分兩種,一種是函數預編譯(函數執行期上下文)和全局預編譯(全局執行期上下文)
函數預編譯我給你們總結了四條規律,不管什麼樣的,都可以正確的避坑
根據這四條法則咱們回到最開始的題目進行講解
例一
function foo(a){
console.log(a);
var a=123;
console.log(a);
function a(){}
console.log(a);
var b=function(){}
console.log(b);
function d(){}
}
foo(1);
/***
* 1.建立AO對象
* AO{
*
* }
* 2.找形參和變量聲明,將變量的形參名做爲AO對象的屬性名,值爲undefined
* AO{
* a:undefined
* b:undefined
* }
* 注:因爲形參a和變量聲明a相同,取其中一個便可
*
* 3.將實參值和形參進行統一
* AO{
* a:undefined=>a:1,
* b:undefined
* }
*
* 4.在函數體裏面找函數聲明,值賦予函數體
*
* AO{
* a:1=>function a(){},
* b:undefined,
* d:function d(){}
* }
* 此時預編譯結束,開始函數執行
*/
根據步驟分析
一、建立AO對象
二、找形參和變量聲明,將變量的形參名做爲AO的屬性名,值爲undefined
形參:a
變量聲明:a,b
因爲形參和變量聲明都是同一個,因此確定出現覆蓋的狀況,取其中一個就能夠了,此時AO對象中含有兩個屬性
AO{
a:undefined,
b:undefined,
}
三、將實參值和形參進行統一
實參值:1
AO{
a:1,
b:undefined
}
此時的a從undefined變成1
四、在函數體裏面找函數聲明,值賦予函數體
函數聲明:function a(){},function d(){}
❝注意:b是函數表達式,不要弄錯了
❞
AO{
a:function a(){},
b:undefined,
d:function d(){}
}
此時預編譯結束,函數開始執行,解釋一行執行一行
第一個輸出function a(){},到了第二個的時候,var a已經提高了,可是a=123沒有調用,因此第二個輸出123
第三個的時候,function a(){}已經進行提高了,因此這一行代碼不要看,直接輸出123,同理b=function (){}賦值也同樣。
❝答案依次是:function a(){},123,123,function (){}
❞
例二
function test(a,b) {
console.log(a);
c=0;
var c;
a=3;
b=2;
console.log(b);
function b() {}
function d() {}
console.log(b);
}
test(1);
/***
* 1.建立AO對象
* AO{
*
* }
* 2.找形參和變量聲明,將變量的形參名做爲AO的屬性名,值爲undefined
* AO{
* a:undefined,
* b:undefined,
* c:undefined,
* }
* 3.將實參值和形參進行統一
* AO{
* a:undefined=>a:1,
* b:undefined,
* c:undefined
* }
* 4.在函數體裏面找函數聲明,值賦予函數體
* AO{
* a:1,
* b:undefined=>function b(){},
* c:undefined,
* d:function d(){}
* }
* 預編譯階段結束,函數開始執行
*
*/
一、建立AO對象
二、找形參和變量聲明,將變量的形參名做爲AO對象的屬性名,值爲undefined
形參:a,b
變量聲明:c
AO{
a:undefined,
b:undefined,
c:undefined,
}
三、將實參值和形參進行統一
實參值:1
AO{
a:1,
b:undefined,
c:undefined,
}
四、在函數體裏面找函數聲明,值賦予函數體
函數聲明:function b(){},function d(){}
AO{
a:1,
b:function b(){},
c:undefined,
d:functiond(){}
}
預編譯階段結束,函數開始執行
❝答案:1,2,2
❞
注意:函數執行上下文發生在函數執行的前一刻
to be honest,全局預編譯和函數預編譯都差很少,少了中間的第三步,將實參值和形參進行統一
函數預編譯發生在函數執行的前一刻,而全局預編譯發生在script標籤建立的時候,能夠看做script是一個大的function。
全局執行上下文和函數執行上下文同時使用纔是真香,一塊兒來看以下兩個示例
例一
console.log(test);
function test(test) {
console.log(test);
var test=234;
console.log(test)
function test() {}
}
test(1);
var test=123;
/***
* 1.建立GO對象
* GO{
*
* }
* 2.找變量聲明,值爲undefined
* GO{
* test:undefined
* }
* 3.找函數聲明,值賦予函數體
* GO{
* test:undefined=>function test(){....此處省略},
* }
* 全局預編譯結束,開始執行代碼
*
*/
/***
* 1.建立AO對象
* AO{
*
* }
* 2.找形參和變量聲明,將形參的變量名做爲AO對象的屬性名,值爲undefined,
* AO{
* test:undefined,
* }
* 3.將實參值和形參值統一
* AO{
* test:undefined=>test:1,
* }
* 4.在函數體裏面找函數聲明,值賦予函數體
* AO{
* test:1=>test:function test(){}
* }
* 函數預編譯結束,開始執行代碼
*
*
*
*/
1、全局預編譯
一、建立GO對象
GO{
}
二、找變量聲明,值爲undefined
變量聲明:test
GO{
test:undefined
}
三、找函數聲明,值賦予函數體
函數聲明:function test(test){....}
GO{
test:function test(test){....}
}
全局預編譯結束,開始執行代碼,進入函數預編譯
2、函數預編譯
一、建立AO對象
AO{
}
二、找形參和變量聲明,將形參的變量名做爲AO的屬性名,值爲undefined
形參:test
變量聲明:test
AO{
test:undefined
}
三、將實參值和形參統一
實參值:1
AO{
test:1
}
四、在函數體裏找函數聲明,值賦予函數體
函數聲明:function test(){}
AO{
test:function test(){}
}
函數預編譯結束,開始執行代碼
❝答案:function test(test){....},function test(){},234
❞
這裏須要多分析全局預編譯,仍是萬變不離其中
例二
global=100;
function fn() {
console.log(global);
global=200;
console.log(global);
var global=300;
}
fn();
var global;
/***
* 1.建立GO對象
* GO{
*
* }
* 2.找變量聲明,值爲undefined
* GO{
* global:undefined
* }
* 3.找函數聲明,值賦予函數體
* GO{
* global:undefined,
* fn:function fn(){...}
* }
*
* 全局預編譯結束,執行代碼,進入函數預編譯
*/
/***
*1.建立AO對象
* AO{
*
* }
* 2.找形參和變量聲明,將形參的變量名做爲AO的屬性名,值爲undefined
* AO{
* global:undefined
* }
* 3.將實參值和形參統一
* AO{
* global:undefined
* }
* 4.在函數體裏面找函數聲明,值賦予函數體
* AO{
* global:undefined
* }
*函數預編譯結束,開始執行代碼
*
*/
1、全局預編譯
一、建立GO對象
GO{
}
二、找變量聲明,值爲undefined
變量聲明:global
GO{
global:undefined
}
三、找函數聲明,值賦予函數體
GO{
global:undefined,
fn:function fn(){.....}
}
全局預編譯完成,執行代碼,進入函數預編譯
2、函數預編譯
一、建立AO對象
AO{
}
二、找形參和變量聲明,將形參的變量名做爲AO對象的屬性名,值爲undefined
形參:沒有
變量聲明:global
AO{
global:undefined
}
三、將實參值和形參統一
實參值:沒有
AO{
global:undefined
}
四、在函數體裏找函數聲明,值賦予函數體
函數聲明:沒有
AO{
global:undefined
}
函數預編譯完成,執行代碼
❝答案:undefined,200
❞
在這裏涉及一點點做用域和做用域鏈的知識,就近原則,本身有就用本身的,本身沒有就看下GO裏面是否有,若是都沒有就是undefined,
函數執行的時候,本身裏面有global,因此就用本身的。
如今回過頭來看如今這兩句話
一、imply global 暗示全局變量,即任何變量,若是變量未經聲明就賦值,此變量就爲全局對象全部
二、一切聲明的全局變量都是window屬性
能夠確認的是GO就是window,從window裏面取值就是從GO裏面取值。
筆者找了一道很是有意思的題目,本身按照步驟也作錯了,因此在這裏分享一下給你們
a=100;
function demo(e) {
function e() {}
arguments[0]=2;
console.log(e);
if(a){
var b=123;
function c() {}
}
var c;
a=10;
var a;
console.log(b);
f=123;
console.log(c);
console.log(a);
}
var a;
demo(1);
console.log(a);
console.log(f);
/***
* 1.建立GO對象
* GO{
*
* }
* 2.着變量聲明,值爲undefined
* GO{
* a:undefined
* }
* 3.找函數聲明,值賦予函數體
* GO{
* a:undefined,
* demo:function demo(e){...}
* }
* 全局預編譯完成,執行代碼進入函數預編譯
*
*/
/***
*1.建立AO對象
* AO{
*
* }
* 2.找形參和變量聲明,將形參的變量名做爲AO對象的屬性名,值爲undefined
* AO{
* e:undefined,
* a:undefined,
* b:undefined,
* c:undefined,
*
* }
* 3.將實參值和新參統一
* AO{
* e:1,
* a:undefined,
* b:undefined,
* c:undefined,
* }
* 4.在函數體裏面找函數聲明,值賦予函數體
* AO{
* e:function e(){},
* a:undefined,
* b:undefined,
* c:function c(){}
* }
* 函數預編譯完成,執行代碼
*/
1、全局預編譯
一、建立GO對象
GO{
}
二、找變量聲明,值爲undefined
變量聲明:a
GO{
a:undefined
}
三、找函數聲明,值賦予函數體
函數聲明:function demo(e){...}
GO{
a:undefined,
demo:function demo(e){...}
}
全局預編譯完成,執行代碼,進入函數預編譯
2、函數預編譯
一、建立AO對象
AO{
}
二、找形參和變量聲明,將形參的變量名做爲AO對象的屬性名,值爲undefined
形參:e
變量聲明:a,b,c
AO{
e:undefined,
a:undefined,
b:undefined,
c:undefined
}
三、將實參值和形參統一
實參值:1
形參:e
AO{
e:1,
a:undefined,
b:undefined,
c:undefined
}
四、在函數體裏面找函數聲明,值賦予函數體
函數聲明:function e(){},function c(){}
AO{
e:function e(){},
a:undefined
b:undefined
c:function c(){}
}
函數預編譯完成,執行代碼
理想答案
❝2,undefined,function c(){},10,100,123
❞
實際答案
❝2,undefined,undefined, 10,100 ,123
❞
因爲谷歌瀏覽器的新規定,不能在if語句裏面定義函數,因此,function c(){}沒法提高
看到這裏的讀者都是最帥的,最美的,本篇是我嘗試寫文章的新作法,第一次搞這種類型的文章,由於但願本身的文章閱讀起來能夠沒有那麼枯燥(能夠從中獲取更多的快樂),學習是一件特別痛苦的事情,看文章也是,我也但願本身的文章能夠更加的生動、幽默。讓看文章的你既能夠學到東西也不會那麼枯燥。
若是您有更好的建議,請在下方留下您寶貴的評論。
若是以爲本篇文章對您有用的話,能夠麻煩您幫忙點亮那個點贊按鈕嗎?
對於二郎神楊戩這個暖男來講:「真的真的很是有用」,您的支持將是我繼續寫文章前進的動力,咱們下篇文章見。