作用域鏈【主要作用:變數名解析】 如果你理解了作用域鏈,那麼閉包對於你而言,理解起來就相當地簡單了。 作用域鏈,可以看成是一個有序檢索的對象列表。 【有序檢索:即就近原則“由近及遠”,全局對象始終是最後一個】 舉例來說,也就是: 如果JS的最頂級代碼(不包含在函數內的代碼),它的作用域鏈——一個全局 ...
作用域鏈【主要作用:變數名解析】
如果你理解了作用域鏈,那麼閉包對於你而言,理解起來就相當地簡單了。
作用域鏈,可以看成是一個有序檢索的對象列表。
【有序檢索:即就近原則“由近及遠”,全局對象始終是最後一個】
舉例來說,也就是:
如果JS的最頂級代碼(不包含在函數內的代碼),它的作用域鏈——一個全局對象;
如果JS代碼中有函數,但是沒嵌套,它的作用域鏈有兩個對象,第一個是函數內的變數即函數參數,第二個就是全局對象【第一第二的順序很重要】;
如果有函數嵌套,那麼作用域鏈至少有3個對象。
【作用域鏈的創建規則】
定義函數的時候,會保存一個作用域鏈;
調用函數的時候,有兩個操作:一是將函數內的局部變數保存在一個新建對象中,然後添加到函數定義時的作用域鏈中;二是會創建一個“運行期上下文”的內部對象,每一個運行期上下文都有一個單獨的作用域鏈,而調用時會將上面提到那個作用域鏈中的對象相當於複製一份到運行期上下文這個作用域鏈中。
在嵌套函數時,有些不一樣,我們每一次調用外部函數的時候,內部函數都需要重新定義一次,因為每次運行外部函數時,它的作用域鏈都是不相同的。內部函數在每次定義時也有微妙的差別——在每次調用外部函數的時候,內部函數的代碼是相同的,而且關聯這段代碼的作用域鏈也不相同。
1 function buttonInit(){ 2 for(var i=1;i<4;i++){ 3 var b=document.getElementById("button"+i); 4 b.addEventListener("click",function(){ 5 alert("Button"+i); },false); 6 } 7 } 8 window.onload=buttonInit; 9 //--結果:都是button4 10 11 12 13 function buttonInit(){ 14 for(var i=1;i<4;i++){ 15 (function a(j) { 16 console.log(j); 17 var b=document.getElementById("button"+j); 18 b.addEventListener("click",function(){ 19 alert("Button"+j); 20 },false); 21 })(i) 22 } 23 } 24 window.onload=buttonInit; 25 //--結果:button1、button2、button3
閉包:【主要作用:變數私有化】
缺點:
1、 閉包會讓函數內部變數始終保存在記憶體中,需要手動釋放資源;
【解決辦法:即將函數設置為null】
2、過多的閉包導致記憶體泄漏。
實現閉包:
當嵌套函數被作為外部函數的返回值返回或者存儲在某處的屬性里,這時會有一個外部引用指向這個嵌套函數,就不會被回收,並且這個嵌套函數所指向的變數綁定對象也不會回收。
var name = 'one'; function a() { var name = 'two'; function f() { return name; } return f; } a()();
function counter(m) { return{ get count(){ return m++; }, set count(n){ if(m<=n) m=n; else throw Error('count can only be set to a larger value'); } }; } var c = counter(100); console.log(c.count);//--100 console.log(c.count);//--101 c.count = 200; console.log(c.count);//--200 console.log(c.count=200);//--throw Error console.log(c.count=300);//--300