如何產(chǎn)生閉包?
- 當(dāng)一個(gè)嵌套的內(nèi)部(子)函數(shù)引用了嵌套的外部(父)函數(shù)的變量(函數(shù))時(shí), 就產(chǎn)生了閉包
閉包是什么?
- 通過chrome工具調(diào)試查看得知: 閉包本質(zhì)是內(nèi)部函數(shù)中的一個(gè)對(duì)象, 這個(gè)對(duì)象中包含引用的變量屬性
- 理解一: 閉包是嵌套的內(nèi)部函數(shù)(絕大部分人)
- 理解二: 包含被引用變量(函數(shù))的對(duì)象(極少數(shù)人)
- 注意: 閉包存在于嵌套的內(nèi)部函數(shù)中
產(chǎn)生閉包的條件?
- 函數(shù)嵌套
- 內(nèi)部函數(shù)引用了外部函數(shù)的數(shù)據(jù)(變量/函數(shù))
常見的閉包
- 將函數(shù)作為另一個(gè)函數(shù)的返回值
function fn1() {
var a = 2;
function fn2() {
a++;
console.log(a);
}
return fn2;
}
var f = fn1();
f();
f();
- 將函數(shù)作為實(shí)參傳遞給另一個(gè)函數(shù)調(diào)用
function showDelay(msg, time) {
setTimeout(function () {
alert(msg)
}, time)
}
showDelay('atguigu', 2000)
閉包的作用
- 使用函數(shù)內(nèi)部的變量在函數(shù)執(zhí)行完后, 仍然存活在內(nèi)存中(延長了局部變量的生命周期)
- 讓函數(shù)外部能操作內(nèi)部的局部變量
閉包的生命周期
- 產(chǎn)生: 在嵌套內(nèi)部函數(shù)定義執(zhí)行完時(shí)就產(chǎn)生了(不是在調(diào)用)
- 死亡: 在嵌套的內(nèi)部函數(shù)成為垃圾對(duì)象時(shí)
閉包的應(yīng)用 :
- 定義JS模塊
- 具有特定功能的js文件
- 將所有的數(shù)據(jù)和功能都封裝在一個(gè)函數(shù)內(nèi)部(私有的)
- 只向外暴露一個(gè)包信n個(gè)方法的對(duì)象或函數(shù)
- 模塊的使用者, 只需要通過模塊暴露的對(duì)象調(diào)用方法來實(shí)現(xiàn)對(duì)應(yīng)的功能
- 循環(huán)遍歷加監(jiān)聽
- JS框架(jQuery)大量使用了閉包
閉包缺點(diǎn)
- 函數(shù)執(zhí)行完后, 函數(shù)內(nèi)的局部變量沒有釋放, 占用內(nèi)存時(shí)間會(huì)變長诬烹,容易造成內(nèi)存泄露
解決:
- 能不用閉包就不用
- 及時(shí)釋放 :
f = null; //讓內(nèi)部函數(shù)對(duì)象成為垃圾對(duì)象
- 閉包會(huì)在父函數(shù)外部可免,改變父函數(shù)內(nèi)部變量的值况芒。
多個(gè)子函數(shù)的[[scope]]
都是同時(shí)指向父級(jí),是完全共享的绷跑。因此當(dāng)父級(jí)的變量對(duì)象被修改時(shí)履怯,所有子函數(shù)都受到影響步咪。
解決:
- 變量可以通過 函數(shù)參數(shù)的形式 傳入,避免使用默認(rèn)的
[[scope]]
向上查找
- 使用
setTimeout
包裹殖演,通過第三個(gè)參數(shù)傳入
- 使用 塊級(jí)作用域,讓變量成為自己上下文的屬性年鸳,避免共享
內(nèi)存溢出與內(nèi)存泄露
- 內(nèi)存溢出
- 一種程序運(yùn)行出現(xiàn)的錯(cuò)誤
- 當(dāng)程序運(yùn)行需要的內(nèi)存超過了剩余的內(nèi)存時(shí), 就出拋出內(nèi)存溢出的錯(cuò)誤
- 內(nèi)存泄露
- 占用的內(nèi)存沒有及時(shí)釋放
- 內(nèi)存泄露積累多了就容易導(dǎo)致內(nèi)存溢出
- 常見的內(nèi)存泄露:
- 意外的全局變量
- 沒有及時(shí)清理的計(jì)時(shí)器或回調(diào)函數(shù)
- 閉包