閉包的實現原理和作用 1、閉包的概念:指有權訪問另一個函數作用域中的變數的函數,一般情況就是在一個函數中包含另一個函數。 2、閉包的作用:訪問函數內部變數、保持函數在環境中一直存在,不會被垃圾回收機制處理 因為函數內部聲明 的變數是局部的,只能在函數內部訪問到,但是函數外部的變數是對函數內部可見的, ...
閉包的實現原理和作用
1、閉包的概念:指有權訪問另一個函數作用域中的變數的函數,一般情況就是在一個函數中包含另一個函數。
2、閉包的作用:訪問函數內部變數、保持函數在環境中一直存在,不會被垃圾回收機制處理
因為函數內部聲明 的變數是局部的,只能在函數內部訪問到,但是函數外部的變數是對函數內部可見的,這就是作用域鏈的特點了。
子級可以向父級查找變數,逐級查找,找到為止
1 function bar(){ 2 //外層函數聲明的變數 3 var value=1; 4 5 function foo(){ 6 console.log(value); 7 } 8 return foo(); 9 }; 10 var bar2=bar; 11 //實際上bar()函數並沒有因為執行完就被垃圾回收機制處理掉 12 //這就是閉包的作用,調用bar()函數,就會執行裡面的foo函數,foo這時就會訪問到外層的變數 13 bar2();
因此我們可以在函數內部再創建一個函數,這樣對內部的函數來說,外層函數的變數都是可見的,然後我們就可以訪問到他的變數了。
3、閉包的優點:
- 方便調用上下文中聲明的局部變數
- 邏輯緊密,可以在一個函數中再創建個函數,避免了傳參的問題
4、閉包的缺點:
因為使用閉包,可以使函數在執行完後不被銷毀,保留在記憶體中,如果大量使用閉包就會造成記憶體泄露,記憶體消耗很大
實際開發中JS閉包的應用
1。在函數外使用函數內的變數 .函數作為返回值 (閉包作用:避免變數被環境污染)
1 function F1(){ 2 var a = 100; 3 return function(){ 4 console.log(a) 5 } 6 } 7 var f1 =F1(); 8 var a = 200; 9 f1()//100
1 function init(){ 2 var name = "hello world";//name是一個被init創建的局部變數 3 function sayName(){//sayName是一個內部函數,閉包 4 alert(name);//使用了父級函數聲明的變數name 5 } 6 sayName(); 7 } 8 init();//"hello world"
2.函數作為參數傳遞
1 function F1(){ 2 var a = 100; 3 return function(){ 4 console.log(a) 5 } 6 } 7 var f1 =F1(); 8 function F2(fn){ 9 var a = 200; 10 fn(); 11 } 12 F2(f1); // 100
3.將函數與其所操作的某些數據關聯起來,通常,你使用只有一個方法的對象的地方,都可以使用閉包
1 // 改變dom樣式 2 document.getElementById("a").onclick = setSize(12); 3 document.getElementById("b").onclick = setSize(18); 4 document.getElementById("c").onclick = setSize(22); 5 function setSize(fontSize){ 6 return function(){ 7 document.body.style.fontSize = fontSize + 'px'; 8 } 9 }
4.用閉包模擬私有方法
1 //這三個公共函數是共用同一個環境的閉包。多虧 JavaScript 的詞法作用域,它們都可以訪問 privateCounter 變數和 changeBy 函數。 2 var makeCounter = function () { 3 var privateCounter = 0; 4 function changeBy(val){ 5 privateCounter += val; 6 }; 7 return { 8 increment: function(){ 9 changeBy(1); 10 }, 11 decrement: function(){ 12 changeBy(-1); 13 }, 14 value: function(){ 15 return privateCounter; 16 } 17 } 18 }; 19 var Counter1 = makeCounter(); 20 var Counter2 = makeCounter(); 21 Counter1.increment(); 22 console.log(Counter1.value());//1 每次調用其中一個計數器時,通過改變這個變數的值,會改變這個閉包的詞法環境。然而在一個閉包內對變數的修改,不會影響到另外一個閉包中的變數。 23 console.log(Counter2.value());//0 以這種方式使用閉包,提供了許多與面向對象編程相關的好處 —— 特別是數據隱藏和封裝。
5.迴圈裡面的閉包
怎麼才能實現輸出0-5呢?
1 for (var i = 0; i < 5; i++) { 2 setTimeout(function () { 3 console.log(i); 4 }, 1000 * i); 5 }//55555
//方法一,makeCallback函數為每一個回調創建一個新的詞法環境。 function makeCallback(i) { return function() { console.log(i) }; } for(var i=0;i<10;i++){ setTimeout(makeCallback(i),1000) }
//另一種方法使用了匿名閉包 for(var i=0;i<10;i++){ (function(i){ setTimeout(function () { console.log(i) },1000) })(i) }
1 //使用let聲明變數 2 for (let i = 0; i < 5; i++) { 3 setTimeout(function () { 4 console.log(i); 5 }, 1000 * i); 6 }