理解闭包(二)

来源:互联网 发布:数控车床外圆锥度编程 编辑:程序博客网 时间:2024/06/07 10:00

闭包与变量

闭包只能取得包含函数中任何变量的最后一个值。闭包保存的是整个变量对象,而不是某个特殊位置的变量。
例如:

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<arr.length;i++){        console.log(arr[i]());     }//10 10 10 ... 10 

这个函数会返回一个函数数组,表面上,似乎每个函数数组内的函数应该返回自己的索引值,即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<arr.length;i++){        console.log(arr[i]());     }//1 2 3 4 5 ...10 

重写函数之后,对应数组中的每一个函数可以返回其对应的索引值。在这个重写的函数中,我们没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋值给数组。这里的匿名函数有一个参数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 window

object.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对象。

原创粉丝点击