理解閉包
1.如何產生閉包
當一個嵌套的內部(子)函數引用了嵌套的外部(父)函數的變量(函數)時,就產生了閉包
2.閉包到底是什麼
閉包是嵌套的內部函數
包含被引用變量(函數)的對象
注意:閉包存在於嵌套的內部函數中
3.產生閉包的條件
函數嵌套
內部函數引用了外部函數的數據(變量/函數)
function fn1() { // 此時閉包就已經產生了(函數提升,內部函數對象已經創建了) var a =2 function fn2() { a++ console.log(a) } return fn2 } var f = fn1() f() // 3 f() // 4 f = null // 閉包死亡(包含閉包的函數對象成爲垃圾對象)
常見的閉包
1.將函數作爲另一個函數的返回值
2.將函數作爲實參傳遞給另一個函數調用
閉包的作用
1.延長局部變量的生命週期
2.從外部可以(間接)操作函數內部的局部變量
閉包的生命週期
1.產生:在嵌套內部函數定義執行完成就產生了(不是在調用時)
2.死亡:在嵌套內部函數成爲垃圾對象時
閉包的應用
自定義JS模塊
*具有特定功能得js文件
*將所有的數據和功能都封裝在一個函數內部(私有的)
*只向外暴露一個包含n個方法的對象或函數
*模塊的使用者,只需要通過模塊暴露的對象調用方法來實現對應的功能
案例一
function myModule() { // 私有變量 var msg = 'Hello World' // 操作私有變量的函數 function upperCase() { console.log('upperCase()' + msg.toUpperCase()) } function lowerCase() { console.log('lowerCase()' + msg.toLowerCase()) } // 向外部暴露對象(給外部使用的方法) return { upperCase: upperCase, lowerCase: lowerCase } }
案例二
(function () { // 私有變量 var msg = 'Hello World' // 操作私有變量的函數 function upperCase() { console.log('upperCase()' + msg.toUpperCase()) } function lowerCase() { console.log('lowerCase()' + msg.toLowerCase()) } // 直接掛在window上 window.myModule = { upperCase: upperCase, lowerCase: lowerCase } })()
閉包的缺點以及解決
1.缺點
*函數執行完後,函數內的局部變量沒有釋放,佔用內存時間變長
*容易造成內存泄漏
2.解決
*能不用閉包就不用
*及時釋放 (賦值null)
閉包代碼案例
代碼一
var name = "window" var obj = { name : 'object', getName : function() { return function() { return this.name; } } } console.log(obj.getName()()) // window
代碼二
var name = "window" var obj = { name : 'object', getName : function() { var that = this return function() { return that.name } } } console.log(obj.getName()()) // object
代碼三
function fun(n, o) { console.log(o) return { fun: function(m) { return fun(m, n) } } } var aa = fun(0); // undefined aa.fun(1); // 0 aa.fun(2); // 0 aa.fun(3); // 0 var bb = fun(0).fun(1).fun(2).fun(3); // undefined 0 1 2 var cc = fun(0).fun(1); // undefined 0 cc.fun(2); // 1 cc.fun(3); // 1
內存溢出與內存泄漏
1.內存溢出
*一種程序運行出現的錯誤
*當程序運行需要的內存超過了剩餘的內存時,就拋出內存溢出的錯誤
2.內存泄漏
*佔用的內存沒有及時釋放
*內存泄漏積累多了就容易導致內存溢出
*常見的內存泄漏
i意外的全局變量
ii沒有及時清理的計時器或回調函數
iii閉包