JavaScript預編譯原理分析
今天用了大量時間復習了作用域、預編譯等等知識
看了非常多博文,翻了翻曾經看過的書(好像好多書都沒有講預編譯)
發現當初認為自己學的非常明確,事實上還是存在一些思維誤區
(非常多博文具有誤導性)
今晚就整理了一下淩亂的思路
先整理一下預編譯的知識吧,日後有時間再把作用域具體解說一下
大家要明確。這個預編譯和傳統的編譯是不一樣的(能夠理解js預編譯為特殊的編譯過程)
JavaScript是解釋型語言,
既然是解釋型語言,就是編譯一行。運行一行
傳統的編譯會經歷非常多步驟,分詞、解析、代碼生成什麽的
日後有時間再給大家科普
以下就給大家分享一下我所理解的JS預編譯
JavaScript運行三部曲
腳本運行js引擎都做了什麽呢?
- 語法分析
- 預編譯
- 解釋運行
在運行代碼前。還有兩個步驟
語法分析非常easy。就是引擎檢查你的代碼有沒有什麽低級的語法錯誤
解釋運行顧名思義便是運行代碼了
預編譯簡單理解就是在內存中開辟一些空間,存放一些變量與函數
理解了預編譯對大家理解作用域相同有幫助
JS預編譯什麽時候發生
我當初思維誤區也發生在這裏
預編譯究竟什麽時候發生
希望大家不要讓上面的運行過程讓你產生誤會,
誤以為預編譯只發生在script內代碼塊運行前
這倒並沒有錯
預編譯確確實實在script代碼內運行前發生了
可是它大部分會發生在函數運行前
JS預編譯實例
舉例前。先來思考一下這幾個概念:
- 變量聲明 var…
- 函數聲明 function…
<script>
var a = 1;// 變量聲明
function b(y){//函數聲明
var x = 1;
console.log(‘so easy‘);
};
var c = function(){//是變量聲明而不是函數聲明!!
//...
}
b(100);
</script>
<script>
var d = 0;
</script>
讓我們看看引擎對這段代碼做了什麽吧
- 頁面產生便創建了GO全局對象(Global Object)(也就是大家熟悉的window對象)
- 第一個腳本文件載入
- 腳本載入完成後。分析語法是否合法
- 開始預編譯
- 查找變量聲明,作為GO屬性。值賦予undefined
- 查找函數聲明。作為GO屬性,值賦予函數體
//偽代碼
GO/window = {
//頁面載入創建GO同一時候。創建了document、navigator、screen等等屬性。此處省略
a: undefined,
c: undefined。
b: function(y){
var x = 1;
console.log(‘so easy‘);
}
}
- 解釋運行代碼(直到運行函數b)
//偽代碼
GO/window = {
//變量隨著運行流得到初始化
a: 1,
c: function(){
//...
},
b: function(y){
var x = 1;
console.log(‘so easy‘);
}
}
- 運行函數b之前。發生預編譯
- 創建AO活動對象(Active Object)
- 查找形參和變量聲明,值賦予undefined
- 實參值賦給形參
- 查找函數聲明,值賦予函數體
//偽代碼
AO = {
//創建AO同一時候。創建了arguments等等屬性。此處省略
y: 100,
x: undefined
}
- 解釋運行函數中代碼
- 第一個腳本文件運行完成。載入第二個腳本文件
- 第二個腳本文件載入完成後,進行語法分析
- 語法分析完成。開始預編譯
- 反復最開始的預編譯步驟……
大家要註意,
預編譯階段發生變量聲明和函數聲明。沒有初始化行為(賦值),匿名函數不參與預編譯
唯獨在解釋運行階段才會進行變量初始化
嗯~最後收一下尾
總結
預編譯(函數運行前)※
1. 創建AO對象(Active Object)
2. 查找函數形參及函數內變量聲明。形參名及變量名作為AO對象的屬性,值為undefined
3. 實參形參相統一,實參值賦給形參
4. 查找函數聲明,函數名作為AO對象的屬性,值為函數引用
預編譯(腳本代碼塊script運行前)
1. 查找全局變量聲明(包含隱式全局變量聲明。省略var聲明),變量名作全局對象的屬性,值為undefined
3. 查找函數聲明。函數名作為全局對象的屬性,值為函數引用
理解了預編譯對理解提升行為,this指向,作用域及性能等問題都有非常大幫助
以後我也會總結這些問題
==主頁傳送門==
JavaScript預編譯原理分析