/* 作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最 后一个值。别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量。下面这个例子可以清晰地说 明这个问题。 */ function createFunctions(){ var result = new Array(); for (var i=0; i < 10; i++){ result[i] = function(){ return i; }; } return result; } /* 这个函数会返回一个函数数组。表面上看,似乎每个函数都应该返自己的索引值,即位置 0 的函数 返回 0,位置 1 的函数返回 1,以此类推。但实际上,每个函数都返回 10。因为每个函数的作用域链中 都保存着 createFunctions() 函数的活动对象,所以它们引用的都是同一个变量 i 。 当 createFunctions()函数返回后,变量 i 的值是 10,此时每个函数都引用着保存变量 i 的同一个变量 对象,所以在每个函数内部 i 的值都是 10。但是,我们可以通过创建另一个匿名函数强制让闭包的行为 符合预期,如下所示。 */ function createFunctions(){ var result = new Array(); for (var i=0; i < 10; i++){ result[i] = function(num){ return function(){ return num; }; }(i); } return result; }
每个函数在被调用时都会自动取得两个特殊变量:this 和 arguments。
1 2 3 4 5 6 7 8 9 10 11 12
把外部作用域中的 this 对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了 var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var that = this; return function(){ return that.name; }; } }; alert(object.getNameFunc()()); //"My Object"
内存泄漏
1 2 3 4 5 6 7 8 9 10
function assignHandler(){ var element = document.getElementById("someElement"); var id = element.id;
element.onclick = function(){ alert(id); };
element = null; }
模仿块级作用域
1 2 3 4 5 6 7 8 9
function outputNumbers(count){ (function () { for (var i=0; i < count; i++){ alert(i); } })();
function MyObject(){ //私有变量和私有函数 var privateVariable = 10; function privateFunction(){ return false; } //特权方法 this.publicMethod = function (){ privateVariable++; return privateFunction(); }; } 这个模式在构造函数内部定义了所有私有变量和函数。然后,又继续创建了能够访问这些私有成员 的特权方法。能够在构造函数中定义特权方法,是因为特权方法作为闭包有权访问在构造函数中定义的 所有变量和函数。对这个例子而言,变量 privateVariable 和函数 privateFunction()只能通过特 权方法 publicMethod()来访问。在创建 MyObject 的实例后,除了使用 publicMethod()这一个途 径外,没有任何办法可以直接访问 privateVariable 和 privateFunction()。
利用私有和特权成员,可以隐藏那些不应该被直接修改的数据,例如: function Person(name){ this.getName = function(){ return name; }; this.setName = function (value) { name = value; }; } var person = new Person("Nicholas"); alert(person.getName()); //"Nicholas" person.setName("Greg"); alert(person.getName()); //"Greg"
Person.prototype.setName = function (value){ name = value; }; })(); var person1 = new Person("Nicholas"); alert(person1.getName()); //"Nicholas" person1.setName("Greg"); alert(person1.getName()); //"Greg" var person2 = new Person("Michael"); alert(person1.getName()); //"Michael" alert(person2.getName()); //"Michael"