閉包是Javascript語言特有的"鏈式作用域"結構(chain scope)變數的作用域有三種:全域作用域和區域作用域以及塊作用域(ES6),,子物件會一級一級地向上尋找所有父物件的變數,所以,父物件的所有變數,對子物件都是可見的,反之則不成立,
閉包:JavaScript高級程式設計里寫閉包是有權訪問另一個函式作用域中的變數的函式,使作用域得到了延長,我們有時候在函式外部需要得到函式內的區域變數,而閉包就是將函式內部和函式外部連接起來的一座橋梁,
閉包的優點:
- 是閉包封住了變數作用域,有效地防止了全域污染
- 可以讀取其他函式內部的變數,讓這些變數的值始終保持在記憶體中,不會隨著函式的結束而自動銷毀,
- 可以很巧妙地實作靜態私有變數、私有函式方法等
閉包的缺點: 由于閉包會使得函式中的變數都被保存在記憶體中,所以存在記憶體泄漏的風險
- 在瀏覽器端可以通過強制重繪解決,對用戶體驗影響不大
- 在服務端,由于 node 的記憶體限制和累積效應,可能會造成行程退出甚至服務器沓機
使用場景 :函式內部變數只初始化一次
解決方法是顯式對外暴露一個介面,專門用以清理變數:
/*1.清除失敗,因為每次先執行mockData()后才會執行閉包方法,所以每次都會在區域作用域創建常量mem*/
function mockData() {
const mem = {name:"lucas",age:22};
return {
clear: () => {
for(let i in mem){
delete mem[i];
}
}, // 顯式暴露清理介面
get: page => {
if (page in mem) {
return mem[page];
}
mem[page] = Math.random();
}
};
}
console.log(mockData().get('name')); //lucas
mockData().clear(); //清理變數
console.log(mockData().get('name')); //lucas
/* 輸出結果
這里執行多次
lucas
這里執行多次
這里執行多次
lucas
*/
/*2.成功清除但代碼不復用*/
const mem = {name:"lucas",age:22}; //卸載外面
function mockData() {
console.log("這里執行多次")
return {
clear: () => {
for(let i in mem){
delete mem[i];
}
}, // 顯式暴露清理介面
get: (page) => {
if (page in mem) {
return mem[page];
}
mem[page] = "dwdwd";
}
};
}
console.log(mockData().get('name')); //lucas
mockData().clear(); //清理變數
console.log(mockData().get('name')); //undefined
/*
這里執行多次
lucas
這里執行多次
這里執行多次
undefined
*/
/*3.最好寫法*/
function mockData() {
const mem = {name:"lucas",age:22};
console.log("執行一次")
return {
clear: () => {
for(let i in mem){
delete mem[i];
}
}, // 顯式暴露清理介面
get: (page) => {
if (page in mem) {
return mem[page];
}
mem[page] = "dwdwd";
}
};
}
var result = mockData(); //實體化函式使之只會設定一次變數值mem
console.log(result.get('name')); //lucas
result.clear(); //清理常量物件
console.log(result.get('name')); //undefined
/*
執行一次
lucas
undefined
*/
例如"變數只初始化一次"這樣的需求可以使用下面的例子
銷毀閉包產生的變數,實作遞增例子1
//通過匿名函式可以實作閉包,簡稱匿名閉包函式
var foo = (function(varr) {
var n = varr||0;
return {
add: function () {
return ++n;
},
clearVariable: function () {
n = null;
}
}
})(20); //由于匿名立即執行函式只會執行一次,所以這里實引數只能傳一次(若需要傳多次請參考例子2)
foo.add() //21
foo.add() //22
foo.add() //23
foo.clearVariable() //n變數被銷毀
foo.add() //1
銷毀閉包產生的變數,實作遞增例子2
/*寫法1*/
function create_counter(initial) {
var x = initial || 0; //變數只會初始化一次
return {
inc: ()=> {
return x++;
},
clear: ()=>{
x=null;
}
}
}
var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13
c2.clear() //x變數被銷毀
c2.inc(); // 1
/*寫法2:這樣寫不方便銷毀變數*/
function create_counter(initial) {
var x = initial || 0; //變數只會初始化一次
function add(){
return x++;
}
return add;
}
var c2 = create_counter(10);
c2(); //11
c2(); //12
c2(); //13
c2() = null; //清除函式也清除了變數
c2() //報錯不存在函式
var c2 = create_counter(20);
c2(); //21
銷毀閉包產生的變數,實作遞增例子3
function Class(){
this.n = 0;
this.func = ()=>{
this.n ++;
return this.n; //閉包產生的變數需手動清除
}
this.clear = ()=>{
return this.n=null; //銷毀函式內部的變數,避免記憶體泄漏
}
}
var obj = new Class();
obj.func() //1
obj.func() //2
obj.func() //3
obj.clear() //n變數被銷毀
obj.func() //1
后者的可擴展性更好. 當需要實作對一個變數的不同操作時, 后一種可以只需要再定義一個不同的函式(也就是簡單線性擴展), 而前一種(閉包)則需要完全重寫,
如果僅僅是做一個簡單的計數器,大可不用這樣麻煩,下面這簡短的代碼就能輕松實作,
var a = 0;
function myFunction(){
a++;
document.getElementById("demo").innerHTML = a;
}
匿名閉包函式
var a = 1;
(function test (){
alert(a);
})()
上面的function都可以稱之為閉包(匿名閉包函式),
閉包其實還有很多實用場景,比如,我們想在頁面上添加一些可以調整字號的按鈕
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
推薦一篇好文https://juejin.im/post/5dc6449ae51d452bd321252c
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/174695.html
標籤:JavaScript
下一篇:vue-路由
