概念:閉包是指有權訪問另外一個函數做用域中變量的函數。html
function createCompareFun(propertyName) { return function (obj1, obj2) { var a = obj1[propertyName]; var b = obj2[propertyName]; if (a < b) { return -1; } if (a > b) { return 1; } else { return 0; } } } //建立匿名函數 var compare = createCompareFun("age"); //調用該函數 var result = compare({age:3},{age:8}); console.log(result); //-1
若是一個閉包能訪問的函數被執行完畢,做用域鏈(本質上是一個指向變量對象的指針列表)被銷燬後,閉包
其活動對象也不會被銷燬,由於它們被閉包引用,知道閉包函數被銷燬後,它們才被銷燬。函數
因爲閉包會攜帶包含它函數的做用域,因此會佔用更多內存,建議能不使用閉包就不要使用閉包。this
做用域鏈的這種配置機制引出了另一個重要問題:閉包只能取得包含函數中任何一個變量的最後一個值。spa
function createFun() { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function () { return i; } } return result; }; function getResult(fun, array) { for (var j = 0; j < fun.length; j++) { array[j] = fun[j](); } return array; } var fun = createFun(); var array = new Array(); var a = getResult(fun,array); console.log(a); //[ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 ]
注意該問題的本質:注意函數實際執行時i變量的值,定義時的值不能說明問題。prototype
解決辦法:指針
function createFun() { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function (num) { return function () { return num; }; }(i); } return result; }; /*var b = function rightNow(a) { return a; }(5); console.log(b);*/
閉包中this的問題:code
var name = "Window"; var obj = { name: "Object", getFun: function () { return function () { return this.name; }; } }; console.log(obj.getFun()()); //Window //解決: var obj2 = { name: "Obj2", getFun: function () { console.log("Fun's name: "+this.name); var v = this.name; return function () { return v; } } } console.log(obj2.getFun()());
var name = "Window"; var obj = { name: "Object", getFun: function () { return this.name; } }; var a = obj.getFun; console.log(a()); //Window console.log(obj.getFun()); //Object
內存泄漏問題:htm
window.onload = function () { function outer() { var element = document.getElementById("testId"); element.onclick = function () { //閉包的做用域鏈中保存了一個html元素 //只要匿名函數存在,element的引用數最少是1,所以它所佔用的內存永遠不會被回收。 console.log(element.id); }; } outer(); //解決 function outer2() { var element = document.getElementById("testId"); var id = element.id; element.onclick = function () { console.log(id); }; //閉包會引用包含函數的整個活動對象,閉包即便不直接引用element, //包含函數的活動對象任然會保存一個引用,因此這步必須有 element = null; } }
ECMAScript很操蛋地沒有塊級做用域:對象
function Afun() { for (var i = 0; i < 10; i++){ console.log(i); } //在外面從新聲明i,並無什麼卵用 var i; console.log("Outer i is "+i); } Afun(); //Outer i is 10
function Afun() { for (var i = 0; i < 10; i++){ console.log(i); } // var i = 100; console.log("Outer i is "+i); } Afun(); //Outer i is 100
能夠用匿名函數來模仿塊級做用域:
function Afun() { //因爲函數聲明後不能直接跟員括號來執行它 //因此咱們用()包裹函數聲明把它轉換一個指向函數的引用 (function () { for (var i = 0; i < 10; i++){ console.log(i); } })(); console.log(i); //致使Error:i is not defined. }
這種技術常常被用來限制在全局做用域中添加過多的變量和函數。
私有變量:函數的參數、局部變量和在函數內部定義的函數。
共有方法 == 特權方法
//在構造函數定義特權方法 function MyObject() { //私有變量 var v = 10; //私有函數 function f() { return false; } //特權方法 this.pm = function () { ++v; return v; } } var o = new MyObject(); //除了pm()沒有任何途徑能訪問到真正的o.v console.log(o.v); //undefined o.v = 166; //假的 console.log(o.v); //166 console.log(o.pm()); //11
也能夠經過在私有做用域中定義私有變量和函數,建立特權方法。
(function () { var v = 10; function f() { return false; } //注意這裏沒有var,是全局的 //爲了保證全局,也沒有使用聲明函數 MyObject = function () { }; MyObject.prototype.pm = function () { v++; return f(); }; })(); console.log(MyObject); console.log(new MyObject().pm()); //false