第一次寫博客,原本是學習jQuery遇到閉包問題,發現並無理解閉包,發現閉包牽扯的知識點太多。複習了一遍(發現本身該記住的全忘了)寫在博客裏,本身也是小白,但願大神們指點迷津,必將感激涕零。
咱們知道
JavaScript有兩個特色:
單線程,解釋型語(翻譯一行,解釋一行)。但其實翻譯一行,解釋一行是最後一部才這樣作,在這以前會有一個語法分析:通篇掃描看有沒有語法錯誤,但不執行,通篇掃描以後,會進行 預編譯 而後 -->解釋一行,執行一行。這就是咱們所說的
js運行三部曲:語法分析 預編譯 解釋執行
沒錯,預編譯開始啦!
test() //VM129:1 Uncaught ReferenceError: test is not defined
console.log(a) //
VM118:1 Uncaught ReferenceError: a is not defined
test(); //456瀏覽器
function test(){閉包
console.log(456);
};
console.log(a); //undefined
var a = 123;
上面四段代碼當咱們執行前兩個的時候報錯,瀏覽器告訴咱們 test 和 a 沒有被定義,然後兩段代碼並無報錯,這就是預編譯。
在學習預編譯的時候咱們老是記住一句話:函數聲明總體提高,變量 聲明提高。
也就是說預編譯會把整個函數放在最最前面。而變量 聲明提高是什麼意思呢?
var a = 123;實際上是變量聲明和賦值的兩個過程;1)var a; 2)a = 123;預編譯只把聲明提高在最前面
console.log(a); //undefined
var a = 123;
--->
var a;
console.log(a); //undefined
a = 123;
--------------------------
test(); //456
function test(){
console.log(456);
}
--->
funciton test(){
console.log(456);
}
test(); // 456
可是光記住這兩句話並不能解決全部的問題。
看一下下面的
console.log(a);
function a(){
}
var a = 123;
想一下打印的是什麼?
竟然是
ƒ a(){
}
再看看下面的更復雜的
console.log(a);
function a(a){
var a = 234;
var a = function(){
}
a();
}
var a = 123;
這個打印出來是什麼呢?
ƒ a(a){
var a = 234;
var a = function(){
}
a();
}
這是爲何呢?
下面來說一下預編譯:
imply global 暗示全局變量:即任何變量。
若是變量未經申明就賦值,此變量就爲全局對象(window)全部。
a = 123;若是 var a = b = 123;在函數裏a就是函數內部變量,b則是全局變量。
一切聲明的全局變量,全是window的屬性(window 就是全局的域):var a = 123;-----> window.a = 123;
使用var聲明的變量會自動被添加到最接近的環境中。
預編譯發生在函數執行的前一刻。
預編譯四部曲:
1.建立AO對象/活動對象(activation object)(執行期上下文)
2.找形參和變量聲明,將變量和形參名做爲AO屬性名,值爲undefined
3.將實參值和形參統一
4.在函數體裏面找到函數聲明,值賦予函數體
由此咱們便知道上面的那兩個例子打印的爲何是那樣的。
下面咱們來看下更復雜的
function fun(a) {
console.log(a);
var a = 123;
console.log(a);
function a() { }
console.log(a);
var b = function () { }
console.log(b);
function d() { }
}
fun(1);
-->
1.生成AO對象
AO{
}
2.找形參和變量聲明,將變量和形參名做爲AO屬性名,值爲undefined
AO{
a: undefined;
b:undefined;
}
3.將實參值和形參統一
AO{
a: 1;
b:undefined;
}
4.在函數體裏面找到函數聲明,值賦予函數體
AO{
a: function a(){};
b:undefined;
d:function(){}
}
預編譯結束
函數執行 AO就像一個創庫同樣,函數執行期間裏面的倉庫也會變化
AO{
a: function a(){};
b:undefined;
d:function(){}
}
function fun(a) {
console.log(a); // ƒ a() { }
var a = 123;
console.log(a); //123
function a() { }
console.log(a); //123
var b = function () { }
console.log(b); //ƒ () { }
function d() { }
}
--------------------------
function test(){
console.log(b);
if(a){
var b = 100;
}
console.log(b);
c = 234;
console.log(c);
}
var a;
test();
a = 10;
console.log(c);
預編譯
全局GO
GO{
a:undefined;
test:function test(){}
}
AO{
b:undefined;
}
------------
執行函數
GO{
a:undefined;--->10
test:function test(){}
c:234
}
AO{
b:undefined;
}
function test(){
console.log(b); //undefined
if(a){
var b = 100;
}
console.log(b); //undefined
c = 234;
console.log(c); //234
}
var a;
test();
a = 10;
console.log(c); //234
做用域 做用域鏈
function test(){};
咱們知道一個函數就像一個房子同樣,這個房子造成單獨的域,
裏面能看到外面的,外面的看不到裏面的,咱們能夠把函數生成的空間叫作做用域那這個做用域究竟是什麼呢?
這個做用域是因函數產生而產生的,每一個對象都有屬性和方法
,函數(function)也是一種特殊的對象,函數能夠有test.name test.prototype ...這些是能夠訪問的
還有一些屬性是不能夠訪問的
隱式屬性僅供JavaScript引擎處理。
好比[[scope]]:指的就是咱們所說的做用域鏈,其中存儲了執行期上下文的集合。
爲何時集合呢?
做用域鏈:是[[scope]]中所存儲的執行期上下文的集合,這個集合呈現鏈式鏈接,咱們把這種鏈接叫作做用域鏈。
做用域鏈本質上是一個指向變量對象的指針列表,他只是引用,但不包含實際變量對象。
test.[[scope]]這裏面存的就是做用域。系統會根據內部的原理去按期調用scope。
上面提到了執行期上下文(前面做用域也提到的AO對象就是這個):當函數執行的前一刻的時候,會建立一個稱爲執行期上下文的內部對象(AO activation object)。一個執行期上下文定義了一個函數執行時的環境
函數每次執行時對應的上下文都是獨一無二的 test(); test();同樣的函數可是執行期上下文並不相同,因此屢次調用一個函數會致使建立多個執行上下文,當函數執行完畢,他所產生的執行上下文會銷燬。
看一下下面的例子
function a(){}
var glob = 100;
a();
當a函數被定義 a.[[scope]]---> 0:GO{} 由於a函數在全局做用域裏,因此他的第一位存的時GO
當a執行執行 a.[[scope]]---> 0:AO{}
1:GO{}
----------------------------------------------
function a(){
function b(){
function c(){}
c();
}
b();
}
a();
a defined a.[[scope]] ---> 0 : GO
a doing a.[[scope]] ---> 0 : a AO
1 : GO
b defined b.[[scope]] ---> 0 : a AO
1 : GO
b doing b.[[scope]] ---> 0 : b AO
1 : a AO
2 : GO
c defined c.[[scope]] ---> 0 : b AO
1 : a AO
2 : GO
b doing c.[[scope]] ---> 0 : c AO
1 : b AO
2 : a AO
3 : GO