先科普:java
1.javaScript是解釋型語言,就是編譯一行,執行一行.....
2.javaScript沒有塊級及做用域......
3.javaScript具備變量和函數聲明提高功能.....
4.AO對象和GO對象....
5.預編譯就是解決代碼執行順序問題,與java語言相似(jvm)....數組
例如:app
(function(a){ console.log(a); var a = 12; console.log(a); function a(){...} console.log(a); var b = function(){...} console.log(b); function d(){...} })(1);
1.首先建立一個GO對象,和AO對象....[由於該當即執行函數是在全局做用域中執行的,當即執行函數執行時建立AO對象]jvm
GO{ AO{ } }
2.找形參和變量聲明,將變量和形參名做爲AO屬性名,值爲undefined....【變量聲明找到了a和b,其實形參也是一種變量聲明,相似於function(a,b){var a,b;},這裏的形參和函數裏的變量聲明a重名了,那麼後一個變量聲明會被忽略...】函數
GO{ AO{ a: undefined, b: undefined } }
3.將實參和形參統一......【實參值賦值給形參】this
GO{ AO{ a: 1, b: undefined } }
4.在函數體裏面找函數聲明,值賦予函數體....【記住,必定是變量聲明在前,函數聲明在後。因此變量聲明提高和函數聲明提高會出現一個前後順序】code
GO{ AO{ a: function(){...}, b: undefined, d: function(){...} } }
預編譯過程結束。對象
而後開始執行:ip
1.執行第一行:輸出function(){...}
2.執行第二行賦值:作用域
GO{ AO{ a: 12, b: undefined, d: function(){...} } }
3.執行第三行:輸出12
4.第四行是函數聲明,預編譯已經執行了,跳過,執行第五行,輸出:12
5.執行第六行賦值:
GO{ AO{ a: 12, b: function(){...}, d: function(){...} } }
6.執行第七行:輸出:function(){...}
7.第八行是函數聲明,跳過,執行完成...接下來銷燬AO,再銷燬GO【這裏是一個js垃圾清理過程,無關本話題】
例如:
做用域鏈......
本身自己屬性找不到就會去查找上一級的做用域中的屬性...層層迭代查詢
直到查到爲止....
js是根據一個叫[[scope]]的屬性查找的,[[scope]]是任何函數(做用域都存在)都默認自帶的屬性,它保存了一個數組值,長度是父級的總長度(做用域包裹的層級,長度至少爲1),直到window爲止,若是查不到,彈出錯誤...
首先[[scope]][0]保存自身預編譯AO,先查找[[scope]][0],保存了該AO或者GO....
不存在時查找[[scope]][1]...
...
注意:[[scope]]屬性不可枚舉,沒法訪問...
例如:
call和apply擴展了做用域....
apply方法能夠‘借用’其餘對象中的方法(簡稱A)完成屬於本身的任務,A只是暫時用於自身,其自己仍是別人的,你不能夠去修改A中的代碼,也沒法修改...在你調用A時,apply中this指向了自身,能夠這樣理解:我使用了你的方法,方法裏的this指向了我,個人屬性(方法)增長了一項(擴展了個人做用域),可是一旦退出apply時(執行完成後),方法依然要還給你,this指向仍是你,個人做用域迴歸原樣....