閉包閉包
閉包函數
函數對象之間能夠經過做用域鏈相互關聯起來,函數體內部的變量均可以保存在函數做用域內spa
這種特性稱作「閉包」。對象
什麼是變量?ip
變量就是爲一切事物賦的一個name;作用域
var的做用,初始化變量。源碼
變量做用域io
程序源碼中定義這個變量的區域就就是變量做用域。(名字放在什麼地方了)console
全局變量擁有全局做用域,在js代碼的任何地方都是有定義的。(這個名字很響亮)function
在函數體內聲明的變量在函數體內有效,他們是局部變量,函數參數也是局部變量他們只在函數體內有定義。(裏層的能拿到的外層的值,外層的拿不到裏層的值)
在函數體內,同名的局部變量優先級要高於同名的全局變量,函數參數也是一個局部變量。
實例1: var scopte = 「gloabl」;
function checkscope(){
var scope = 「local」;
return scope;
}
checkscope() // 「local」;
實例2:var attri = 100; //全局變量與函數參數同名,全局變量被遮蓋。
function fuck( attri ){
alert(attri);
}
fuck(10); //10
實例3:
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function nested(){
var scope = "nested scope";
return scope;
}
return nested();
}
checkscope(); // 「nested」;
js中沒有塊級做用域,取而代之的是函數做用域。
變量在聲明的函數體以及這個函數體嵌套的任意函數體內都是有定義的。
聲明提早 (遇到var的地方就會聲明提早)
實例4:
function test(o){
/*i在整個函數體內均是由定義的*/
var i = 0;
if( typeof o=="object"){
/*j 在函數體內是用定義的,不單單是在這個代碼段*/
var j=0;
/*k在函數體內是有定義的,不單單是在循環內*/
for( var k=0; k<10;k++){
console.log(k); //輸出0~9
}
console.log(k); //10
}
console.log(j); //j已經定義了,但可能沒有初始化。
}
等價於
function test(o){
var i;
var j;
var k;
i = 0;
if( typeof o=="object"){
j=0;
for( k=0; k<10;k++){
console.log(k);
}
console.log(k);
}
console.log(j);
實例5:
var scope = "global";
function f(){
console.log(scope); //調用f()輸出undefined聲明提早
var scope = "local"; //雖然不是第一行代碼,可是實際scope已經聲明瞭。
console.log(scope) //調用f()輸出local;
}
等價於
var scope = 「global」;
function f(){
var scope ;
console.log(scope);
scope = 「local」;
console.log(scope);js中沒有塊級做用域,取而代之的是函數做用域。
}
做用域鏈( 做用域鏈是特殊的對象列表object-List )
定義函數的是很建立做用域對象,建立的做用域對象組成做用域鏈。
談論做用域的時候要聲明是哪段代碼。就像咱們某人的時候要說明是哪個人。0
每一段JavaScript代碼都有一個與之關聯的做用域鏈,(js中沒有塊級做用域,取而代之的是函數做用域。)這個做用域鏈是一個對象列表。對象列表中定義了這段代碼做用域中的對象。
var 建立的變量時不可配置的,不可經過delete操做符刪除。
全局變量在程序中始終是有定義的,局部變量在聲明的函數體內以及所嵌套的函數體內始終是由定義的。
若是將局部變量看作自定義對象的屬性的話,能夠換個角度來解讀變量做用域,每一段js代碼都有一個與之關聯的做用域鏈,做用域鏈是一個對象列表(object-List),object-List定義了這段碼做用域中的變量。當js須要變量x的時候(這個時候稱做變量解析),它會從object-List中第一個對象開始查找,若是用這個變量x就直接使用,沒有查找下一個object-List中的下一個對象,若是查詢了全部的object-List對象中都沒有x這個變量,則拋出一個引用錯誤。
做用域鏈類型
(1)js的最頂層代碼中(不包含任何函數定義內的代碼),保存的做用域鏈由1個全局對象組成。
(2)在定義不包含嵌套的函數體內,保存的做用域鏈由2個對象組成,第1個對象是定義函數參數和局部變量組成的,第2個是全局對象。
(3)在定義一個嵌套的函數體內,保存的做用域鏈上至少包含3個對象,第1個是嵌套函數內的參數和局部變量組成的對象,第2個對象是外層函數的參數和局部變量組成的,第3個對象是全局對象。
做用域鏈的建立規則
當定義一個function時,這個function實際上保存了一個做用域鏈object-List,當調用這個function時,這個function建立一個新的object(相似於經過構造函數建立對象)來保存它本身的局部變量,而後將這個建立的object添加到保存的那個做用域鏈object-List上,原來的object-List就增長了一個對象,
function a(){ var c ; } //建立這個函數的時候保存了一個object-List
a() //調用這個函數的時候建立了一個新的object來保存本身的局部變量,並將這個對象添加到做用域鏈object-List中,使定義該函數時的做用域鏈變長,而後建立一個新的更長的表示函數調用做用域的「鏈」。
實例7:
var i =0;
/*定義這個function的時候有2個做用域鏈 object-1爲空,object-2 有var i這個變量值。*/
function a(){
i++;
alert(i);
}
/*調用這個函數的時候會建立一個變量對象改變定義function時候的做用域鏈,object-1爲空,object-2var i 發生改變*/
a(); //1
a(); //2
對於嵌套函數來說,每次調用外部函數,都會使定義該外部函數時的做用域鏈變長,因此內部嵌套函數每次的做用域鏈也會微妙的不一樣。嵌套的裏層函數做用域構成包含了外層函數的做用域鏈,外層函數的做用域發生改變,裏層函數的做用域鏈也會開始改變。
調用函數是什麼意義?