1. 程式人生 > >js---作用域鏈,立即執行函式,閉包

js---作用域鏈,立即執行函式,閉包

1.作用域鏈----函式 定義時會獲得 父級的作用域鏈的值放在自己的 [[Scopes]]屬性中, [[Scopes]]是系統自帶的隱式屬性,通過console.dir(函式名)   可以檢視這個函式的作用域鏈,即 [[Scopes]]。任何函式都至少會有Global的作用域,巢狀越深,作用域鏈越長。當執行函式的前一刻時,會生成當前函式的獨一無二的AO,新增到自己的作用域鏈中。
注意:任何函式只能使用自己作用域鏈裡的東西,並且是按照作用域鏈中的由頂向下檢視(順序:自己的作用域--父級作用域---...---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列印的就是當前的索引值