js代码执行过程
當(dāng)瀏覽器加載了js代碼之后,發(fā)生什么?js引擎怎么工作?編譯器做了什么?
以及執(zhí)行環(huán)境對象、作用域鏈、活動對象、變量對象 是什么?
以及作用域鏈?zhǔn)裁磿r候創(chuàng)建、銷毀?等等,這些對象的生命周期都會在js代碼執(zhí)行過程得到一一的體現(xiàn)
先粗淺地了解下流程:
<script src='app.js'></script>
<script>
var name = "Tom";
function getInfo(){
return name;
}
getInfo();
</script>
<script>
var sex = 'male';
</script>
1. 瀏覽器載入第一個代碼段后,開始進行語法檢測(書寫是否正確等);之后,如果還有代碼段,繼續(xù)載入,語法檢查;直到?jīng)]有了代碼段;
2. js引擎 創(chuàng)建全局執(zhí)行環(huán)境,同時生成作用域鏈、變量對象;將全局執(zhí)行對象棧入環(huán)境棧中;(在瀏覽器中全局執(zhí)行對象為window;全局執(zhí)行環(huán)境在退出程序時銷毀;)
3. 編譯器開始對全局執(zhí)行環(huán)境中的var/function聲明進行預(yù)解析;如果全局執(zhí)行環(huán)境的變量對象中沒有name、getInfo等,就添加它們
4. 編譯器預(yù)解析后,生成代碼;js引擎開始執(zhí)行代碼;
以上幾個步驟是非常粗淺的,省卻了很多細節(jié),但是js代碼從加載到瀏覽器到執(zhí)行完,基本就是這個過程;
下面結(jié)合代碼、圖表、解釋等重新、詳細地講解下js代碼的執(zhí)行過程,這里舉兩個例子,一個是基本簡單的流程,一個則涉及到閉包;
一個基本簡單的例子:
var name = "Tom";
function getInfo(){
var nation = "China";
return name + nation;
}
getInfo();
1. 編譯器進行語法檢測
2. js引擎創(chuàng)建全局執(zhí)行環(huán)境對象window,此時生成作用域鏈scope1,變量對象
3. 編譯器 對 var/function 聲明的變量、函數(shù)預(yù)處理;將聲明的變量name添加到變量對象中,值為undefined;
function聲明的函數(shù)變量添加到變量對象中,同時將作用域鏈scope1保存在[[scope]]屬性中;
同時,將全局執(zhí)行環(huán)境棧入環(huán)境棧中;
注1:函數(shù)的作用域鏈在函數(shù)聲明時就已經(jīng)創(chuàng)建,保存在內(nèi)部屬性[[scope]]中;在函數(shù)調(diào)用的時候會再次創(chuàng)建作用域鏈,用于延伸;
注2:此時編譯器沒有對getInfo函數(shù)體做任何操作,
注3:對var/function聲明的變量預(yù)處理時,涉及到聲明提前,函數(shù)聲明優(yōu)先的規(guī)則
這一步處理完之后的代碼相當(dāng)于:
1 function getInfo(){
2 // 編譯器此時沒有對函數(shù)體中操作;
3 // 只有函數(shù)被調(diào)用時,編譯器才會處理函數(shù)體
4 var nation = "China";
5
6 return name + nation;
7
8 }
9
10 var name;
11
12 name = "Tom";
13
14 getInfo();
4. 編譯器生成代碼,js引擎開始執(zhí)行代碼
4.1 代碼執(zhí)行到12行時,js引擎 找到環(huán)境棧中頂部執(zhí)行對象, 然后根據(jù)作用域鏈查找變量對象,發(fā)現(xiàn)變量對象中存在name,則賦值name="Tom"
4.2 代碼執(zhí)行到14行時,js引擎調(diào)用getInfo函數(shù)
4.2.1 創(chuàng)建getInfo執(zhí)行環(huán)境,同時生成作用域鏈,活動對象;
此時生成的作用域鏈會將函數(shù)[[scope]]保存的scope1復(fù)制過來,再添加上新生成的getInfo活動對象;
活動對象在生成時,會生成arguments對象;
注:變量對象、活動對象區(qū)別
變量對象:在生成全局執(zhí)行環(huán)境時生成,不包含arguments
活動對象:在調(diào)用函數(shù),生成函數(shù)執(zhí)行環(huán)境時生成,包含arguments對象
講述到現(xiàn)在一直沒有出現(xiàn)作用域的概念,本人認為變量對象、活動對象就可以看成作用域;
4.2.2 將getInfo執(zhí)行環(huán)境對象棧入環(huán)境棧中,編譯器開始對 var/function聲明的變量預(yù)解析
預(yù)解析后,相當(dāng)于下面的代碼:
1 function getInfo(){
2
3 var nation;
4
5 nation = "China";
6
7 return name + nation;
8
9 }
10
11 var name;
12
13 name = "Tom";
14
15 getInfo();
4.2.3 編譯器生成代碼,js引擎開始執(zhí)行
4.2.3.1 執(zhí)行到第5行,通過作用域鏈找到nation,然后賦值;
4.2.3.2 執(zhí)行到第7行, 通過作用域鏈找到name,然后返回值
4.2.3.3 函數(shù)執(zhí)行完,銷毀getInfo活動對象、作用域鏈,將getInfo執(zhí)行環(huán)境棧出,銷毀;控制權(quán)交給環(huán)境棧中最頂部的執(zhí)行環(huán)境;
基本簡單的流程就是這樣,如果有閉包,即使沒有調(diào)用閉包,閉包[[scope]]指向的作用域鏈涉及的變量對象、活動對象不會銷毀;
一個閉包相關(guān)的流程:
var name = "Tom";
function getInfo(){
var nation = "China";
function sumIn(){
return name + nation;
}
return sumIn();
}
getInfo();
一個相對復(fù)雜的流程:
var name="Hello World";
var obj = {
name:'obj Object',
getName:function(){
console.log( name );
console.log(this.name);
}
}
obj.getName();
明晰這個流程,必須對作用域鏈非常了解;作用域鏈只與函數(shù)相關(guān),跟對象無關(guān);
總結(jié)
- 上一篇: 13. The Security Fil
- 下一篇: 怎么修改路由器lan口地址如何修改新路由