1、Js之前沒有塊級做用域,不過在ES6中有let了。html
2、Js使用函數做用域閉包
function aaa(){ var a = "a"; } console.log(a);//報錯
3、聲明提早函數
console.log(aaa)//報錯
console.log(aaa);//undefined 聲明未定義 var aaa;
console.log(aaa); //undefined 聲明未定義 var aaa = "aaa";
4、Js的做用域鏈spa
function Fun(){ var aaa = "aaa", fun = "fun"; function Inter(){ var aaa = "ccc"; console.log(aaa); //ccc console.log(fun); //fun } Inter(); } Fun();
Inter的做用域鏈翻譯
找到就中止查找返回數據,找不到就延做用域鏈查找,直到Global也查不到就返回報錯;debug
5、Js的做用域鏈在執行前已經被建立rest
1、閉包:是指有權訪問另外一個函數做用域中的變量的函數;code
2、閉包的特色:局部變量不會被垃圾回收;htm
//定義一個函數時,實際是保存它的做用域鏈。當調用這個函數時,會建立一個新的對象來保存局部變量,而且把這個新的對象添加到做用域鏈上。當函數返回時,將做用域鏈中的這個對象刪除。對象
//若是這個函數定義了嵌套函數,並將它做爲返回值返回或者存儲在某處的屬性裏,就會有一個外部的引用指向這個嵌套函數。
3、上面那句是書上的話,很差理解。我翻譯下就是父函數內建立的變量,子函數調用。只要子函數沒有被銷燬,父函數和建立的變量就不會被銷燬。
舉個栗子
function counter(){ var a = 0; function rest(){ console.log(a++); } return rest; } var scope = counter(); scope();//0 scope();//1 scope();//2
建立閉包的常見方式,就是在一個函數內部建立另外一個函數,外部函數將內部函數做爲返回值返回。有的時候這個外部是全局。
for(var i = 0;i<elements.length;i++){ elements[i].onclick = function(){ console.log(i); //elements.length } }
上面每次點擊,輸出的都是elements.length。由於onclick是一個點擊的函數,不能被銷燬,因此i也不能被銷燬保存在Global中。因此點擊輸出的是elements.length。
for(var i = 0;i<elements.length;i++){ function F(n){ elements[n].onclick = function(){ console.log(n); } } F(i); }
這樣修改下就能夠了。
4、垃圾回收機制:若是一個對象再也不被引用,這個對象會被GC回收。若是兩個對象相互引用,這兩個都會被回收。若是a被b引用,b又被c引用,就不會被回收,就是閉包第一個例子的緣由。
5、閉包的兩種寫法
function counter(){ var a = 0; function rest(){debugger return a; } return rest(); } counter();
定義調用在同一做用域
function counter(){ var a = 0; function rest(){debugger return a; } return rest; } //counter()(); var scopt = counter(); scopt();
定義調用不在同一做用域