當一個嵌套的內部(子)函數引用了嵌套的外部(父)函數的變量(函數)時, 就產生了閉包(closure)javascript
使用chrome調試查看java
理解一: 閉包是嵌套的內部函數chrome
理解二: 包含被引用變量(函數)的對象閉包
注意: 閉包存在於嵌套的內部函數中異步
函數嵌套函數
內部函數引用了外部函數的數據(變量/函數)性能
// 1. 將函數做爲另外一個函數的返回值 function fn1() { var num = 10; function fn2() { num++; console.log(num); } return fn2; } // 經過全局變量引用, 保住了內部函數fn2的命 var f = fn1(); f(); // 11 在外部函數執行完成後, 還能夠執行內部函數 f(); //
// 2. 將函數的形參做爲實參傳遞給另外一個函數調用 function logMsgDelay(msg, time) { setTimeout(function () { console.log(msg); }, time) }
封閉做用域又稱值爲封閉空間,還有一個暱稱叫小閉包,以及匿名函數自調。this
<button>按鈕1</button> <button>按鈕2</button> <button>按鈕3</button> /* 封閉做用域又稱值爲封閉空間,還有一個暱稱叫小閉包,以及匿名函數自調。 寫法: (function(){})(); ;(function(){})(); +(function(){})(); -(function(){})(); */ var btns = document.getElementsByTagName('button'); /* 藉助小閉包, 把每次循環的i值都封閉起來 */ for (var i = 0; i < btns.length; i++) { console.log('全局的i:' + i); (function (i) { console.log('局部的i:' + i); var btn = btns[i]; btn.onclick = function () { alert('第' + (i + 1) + '個') } })(i); }
做用域鏈條
JS中有不少做用域, 好比: 全局做用域 和 局部做用域指針
把一些不須要暴露在全局的變量封裝成"私有變量"調試
MyTool1.js
function myTool() { // 1.私有數據 var money = 1000; // 2. 操做數據的函數 function get() { money++; console.log('賺了一筆錢, 總資產: ' + money + '元'); } function send() { money--; console.log('花了一筆錢, 總資產: '+ money + '元'); } //向外暴露對象(給外部使用的方法) return { 'get': get, 'send': send } }
調用
<script type="text/javascript" src="js/MyTool1.js"></script> <script type="text/javascript"> var tool = myTool(); tool.get(); tool.send(); </script>
MyTool2.js
;(function (window) { // 1.私有數據 var money = 1000; // 2. 操做數據的函數 function get() { money++; console.log('賺了一筆錢, 總資產: ' + money + '元'); } function send() { money--; console.log('花了一筆錢, 總資產: '+ money + '元'); } //向外暴露對象(給外部使用的方法) window.myTool = { get: get, send: send } })(window); /* 性能考慮, 做用域鏈條是遞歸查找對象的 壓縮考慮, a,b,c,... */
調用
<script type="text/javascript" src="js/MyTool2.js"></script> <script type="text/javascript"> myTool.get(); myTool.send(); </script>
2個for循環,改成設置1個,根據下標清除
// window.onload = function () { // var allLis = document.getElementsByTagName('li'); // for(var i=0; i<allLis.length; i++){ // var li = allLis[i]; // li.onmouseover = function () { // for(var j=0; j<allLis.length; j++){ // allLis[j].className = ''; // } // this.className = 'current'; // } // } // } window.onload = function () { var allLis = document.getElementsByTagName('li'); // 記錄移動前選中li對應的索引 var preSelectLiIndex = 0; for(var i=0; i<allLis.length; i++){ (function (i) { var li = allLis[i]; li.onmouseover = function () { // 清除 allLis[preSelectLiIndex].className = ''; // 設置 this.className = 'current'; // 賦值 preSelectLiIndex = i; } })(i); } }
前面的timer做爲全局變量,window指針指向它,若是有不少,影響性能。
/* var timer = null; window.onresize = function () { clearTimeout(timer); timer = setTimeout(function () { console.log('輸出的內容!!!!'); }, 200); } */ window.onresize = throttle(function () { console.log('你們好!!!'); }, 200); function throttle(fn, delay) { var timer = null; return function () { clearTimeout(timer); timer = setTimeout(fn, delay); } }
function fn1() { var arr = new Array[999999999]; function fn2() { console.log(arr.length) } return fn2 } var f = fn1(); f(); f = null //讓內部函數成爲垃圾對象-->回收閉包
一種程序運行出現的錯誤
當程序運行須要的內存超過了剩餘的內存時, 就出拋出內存溢出的錯誤
var arrObj = {}; for (var i = 0; i < 10000; i++) { arrObj[i] = new Array(9999999999999); console.log(arrObj); }
佔用的內存沒有及時釋放
內存泄露積累多了就容易致使內存溢出
常見的內存泄露:
1. 佔用內存很大的全局變量
2. 沒有及時清理的計時器/定時器
3. 閉包
// 2. 內存泄露 // 2.1 佔用內存很大的全局變量 /* var num = new Array(9999999999999); console.log(num); */ // 2.2 沒有及時清理的計時器或回調函數 /* var intervalId = setInterval(function () { //啓動循環定時器後不清理 console.log('----') }, 1000); clearInterval(intervalId); */ // 2.3 閉包 /*function fn1() { var num = 111; function fn2() { console.log(num--); } return fn2 } var f = fn1(); f();*/ // f = null