js---作用域鏈,立即執行函式,閉包
阿新 • • 發佈:2018-11-12
1.作用域鏈----函式
定義時會獲得
父級的作用域鏈的值放在自己的
[[Scopes]]屬性中,
[[Scopes]]是系統自帶的隱式屬性,通過console.dir(函式名) 可以檢視這個函式的作用域鏈,即
[[Scopes]]。任何函式都至少會有Global的作用域,巢狀越深,作用域鏈越長。當執行函式的前一刻時,會生成當前函式的獨一無二的AO,新增到自己的作用域鏈中。
注意:任何函式只能使用自己作用域鏈裡的東西,並且是按照作用域鏈中的由頂向下檢視(順序:自己的作用域--父級作用域---...---Gobal 也就是說每次新增都是在第一位新增,其他已經存在的順次向下移),一旦找到就停止尋找。
注意:任何函式只能使用自己作用域鏈裡的東西,並且是按照作用域鏈中的由頂向下檢視(順序:自己的作用域--父級作用域---...---Gobal 也就是說每次新增都是在第一位新增,其他已經存在的順次向下移),一旦找到就停止尋找。
var a={ init:function(){ var num=123; this.test();//報錯---注意作用域鏈是在函式定義的時候獲得父級作用域鏈的,此處test不是在init裡定義的,作用域鏈上只有全域性的作用域 }, test:function(){ console.log(num); } } a.init(); //類似於以下情況 function a(){ var num=0; b(); } function b(){ console.log(num); } a();//報錯---num is not defined
2.立即執行函式(針對初始化功能使用的函式,也可以在專案中起區塊作用,避免團隊變數函式名稱重複相互影響)
(function a(){console.log(1)}());//1 再呼叫a()---報錯 (function b(a,c){console.log(a,c)})(1,2);//1 2 再呼叫b()---報錯 //所以函式名失去意義,上面的函式和下面的函式是作用相同的 (function (){console.log(1)}());//1 (function (){console.log(2)})();//2 //函式只要變成表示式的形式,定義的時候而不是執行的時候,就會發生失去函式名 +function c(){console.log(11)}();//報錯---c is not defined var d=(1+4,2+1); console.log(d) //3 逗號表示計算第一個表示式,然後再計算後一個表示式並且覆蓋前一個的結果
var a=(function aa(){return 1},+function bb(){return 2});
console.log(a); //NaN 第二個表示式的結果
var a=(function aa(){return 1},function bb(){return 2});
console.log(a); //function bb(){return 2} a(); //2bb(); //報錯,var a=(function bb(){}) 是表示式,使bb這個名字失去意義
var cc=1;
if(function c(){}){//除了0 undefined null "" false NaN,其他轉成boolean都是true (function c(){})---表示式 導致c相當於未定義
console.log(typeof c+cc);//"undefined1" //typeof undefined 只有typeof 一個未宣告定義的變數不會報錯,是"undefined".
}
3.閉包---由於子函式將繼承父級AO到自己的作用域鏈中,當子集函式的生命週期超過父級函式的生命週期時會產生閉包。
通俗點來說,就是父級函式已經執行完畢,父函式執行的過程中:子函式被定義獲得父級函式的AO後,被儲存到外部。使子函式變成一個與父函式同級但是擁有父函式AO的函式。
閉包的作用:實現公有變數:函式累加器; 可以做快取,儲存結構;封裝,屬性私有化;模組化開發,防止變數汙染。
function aa(){
var num=0;
function bb(){
num++;
console.log(num);
}
num=123;
return bb;
}
var cc=aa();
cc();//124 說明bb和aa是共同使用一個AO,不是複製一份給子函式,是直接公用
cc();//125 函式執行的時候回產生唯一的獨一無二的AO,但是閉包中使用的父級的AO是確定的,不會再產生新的,因為父級函式並沒有執行,所以是125不是124。
//閉包的小栗子
function bibao(){
var arr=[];
for(var i=0;i<5;i++){
//賦值的時候arr[i]中的i是當前迴圈的i
arr[i]=function(){
console.log(i);
}
}
return arr;//最後的arr=[function(){console.log(i)},...,function(){console.log(i)}]
}
var demo=bibao();
demo.forEach(function(item){
item();//5..5 結果是5個5 item()是function(){console.log(i)} 此時的i是bibao執行時的獨一無二的AO中的i的值,迴圈結束的時候就已經是5了
});
//在求索引的時候很容易出現閉包問題,例如
function indexBiBao(){
var list=document.getElementsByTagName('li');//假設HTML中有一個ul下有四個li 要給li新增點選事件
for(var i=0;i<4;i++){
list[i].addEventListener('click',function(event){
console.log(i);
});
}
}
indexBiBao();//最後點選每個li列印的都是4,因為點選事件被繫結在li上,肯定是在外部了,都不在script標籤裡了。
//也就是點選li的時候去執行這個函式,而此時的i應該在indexBiBao執行結束的時候就變成了4
//解決方法
function indexBiBao(){
var list=document.getElementsByTagName('li');//假設HTML中有一個ul下有四個li 要給li新增點選事件
for(var i=0;i<4;i++){
(function(j){
//接收形參 類似於var j;
//實參賦值 j=i 將當前的i儲存在立即執行函式的AO裡面,在外面呼叫時會直接呼叫父級AO的j
list[j].addEventListener('click',function(event){
console.log(j);
});
})(i);
}
})
}indexBiBao();//最後點選每個li列印的就是當前的索引值