闭包与变量
闭包只能取得包含函数中任何变量的最后一个值。闭包保存的是整个变量对象,而不是某个特殊位置的变量。
例如:
function createFunctions(){ var result = new Array(); for(var i = 0;i<10;i++){ result[i] =function(){ return i; } } return result; } var arr = createFunctions();//接收函数返回的数组 console.log(arr);//[f(),f()...f()] for(var i = 0;i这个函数会返回一个函数数组,表面上,似乎每个函数数组内的函数应该返回自己的索引值,即0位置返回0,但其实每个函数都返回10 因为每个函数的作用域链中都保存着createFunctions()函数的活动对象,它们引用的都是同一个变量i.(JS中无块级作用域,for循环中定义的i最终会保存在createFunctions函数的执行环境中,i的值为10),所以当result数组的每一项接收到一个匿名函数,当在函数外部调用它的时候,沿着作用域链寻找i,其外部的函数活动对象中保存着i的值,为10,所以,调用数组中每一个函数的返回结果都是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; } var arr = createFunctions(); console.log(arr);//[f(),f()...f()] for(var i = 0;i重写函数之后,对应数组中的每一个函数可以返回其对应的索引值。在这个重写的函数中,我们没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋值给数组。这里的匿名函数有一个参数num,也就是函数最终要返回的值。在调用每一个匿名函数的时候,我们传入了变量i,由于函数参数是按值传递的,所以将变量i的当前值复制给参数num。而在这个匿名函数内部,又创建了一个可以访问num的闭包,这样一来,result数组中的每一函数都有自己num变量的一个变量副本,因此就可以返回不同的值。核心,在于利用变量num保存了当前i值的副本。
闭包与this对象
在闭包中使用this对象也可能会导致一些问题。this变量是在运行时基于函数的执行环境绑定的。在全局函数中,this等于window,而当函数被当做某个对象的方法调用时,this等于那个对象。匿名函数的执行具有全局性,因此this对象通常指向window对象,但有时候由于闭包特殊编写方式,导致这一点不是特别明显。 典型案例:
var name = "The window"; var object = { name : "My Object", getNameFunc:function(){ return function(){ return this.name; }; } }; console.log(object.getNameFunc()());//The windowobject.getNameFunc相当于函数
function(){ return function(){ return this.name; }这个函数的返回值为一个 匿名函数,所以object.getNameFunc()相当于匿名函数
function(){ return this.name; }object.getNameFunc()() 相当于在全局环境中运行一个匿名函数,所以this指代的是window对象
如果我们想访问到匿名函数外部函数活动对象,应该怎么办?
我们可以将外部作用域的this对象保存在一个闭包可以访问到的变量里
var name = "The window"; var object = { name : "My Object", var that = this; getNameFunc:function(){ return function(){ return that.name; }; } }; console.log(object.getNameFunc()());//My Object在定义匿名函数之前,我们把this对象赋值给一个名叫that的变量。而在定义了闭包之后,也可以访问这个变量。因为that变量保存了对外部函数对象的引用。
存在几种特殊情况,this的值也可能会意外的改变:var name = "The window"; var object = { name:"My object", getName:function(){ return this.name; } } console.log(object.getName());//My object console.log((object.getName)());//My object var result = (object.getName = object.getName)();//The window console.log(result);第一行输出代码 this.name 就相当于object.name
第二行输出代码 虽然在调用方法之前加上了一个括号,但是object.getName 和(object.getName)的定义是相同的。
第三行输出代码,先执行了一条赋值语句,然后再调用赋值后的结果。因为这个赋值表达式的本身是函数本身,所以this的值无法维持,相当于在全局执行环境中调用这个函数本身,this 指代的对象是window对象。