js 闭包

闭包

定义:闭包是指有权访问另一个函数作用域中的变量的函数。
创建:创建闭包的常见方式,就是在一个函数内部创建另一个函数。
理解

function createComparisonFunction(propertyName){
    return function(object1,object2){ //闭包
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
        if(value1 < value2){
            return -1;
        }else if(value1 > value2){
            return 1;
        }else{
            return 0;
        }
    };
}

如上例子中,代码第3、4行表示内部函数(一个匿名函数)可以访问外部函数中的变量propertyName;因为内部函数的作用域链中包含createComparisonFunction()的作用域。

//创建函数
var compareNames = createComparisonFunction("name");
//调用函数
var result = compareNames({name:"AA"},{name:"BB"});
console.log(compareNames);//f(object1,object2){...}
console.log(result);//-1
//解除对匿名函数的引用(以便释放内存)
compareNames = null;

匿名函数从createComparisonFunction()中被返回后,它的作用域链被初始化包含createComparisonFunction()函数的活动对象和全局变量对象,所以匿名函数可以访问在createComparisonFunction()中定义的所有变量;
createComparisonFunction()函数在执行完毕后,其活动对象不会被销毁仍留在内存中,因为匿名函数作用域链仍在引用这个活动对象;(将compareNames设置为null可以解除对匿名函数的引用,匿名函数作用域被销毁则createComparisonFunction函数作用域也被销毁)
下图7-2展示了调用compareNames()的过程产生的作用域链间的关系。

image.png

缺点:由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存;过度使用闭包可能会导致内存占用过多。

闭包与变量

定义:闭包只能取得包含函数中任何变量的最后一个值。
原因:闭包所保存的是整个变量对象,而不是某个特殊的变量。
理解

function createFunctions(){
    var result = new Array();
    for(var i=0;i<10;i++){
        result[i] = function(){
            return i;
        };
    }
    return result;
}
var a = createFunctions();
a.forEach(e=>{
    console.log(e());
})//输出10个10

如上代码,全局变量a的值为一个函数数组,表面上看每个函数都应该返回自己的索引值,实际上每个函数都返回10;因为每个函数的作用域链中都保存的是对createFunctions()函数活动变量的引用,它们引用的是同一个变量i,当createFunctions()函数返回后i的值为10,此时每个函数内部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;
}
var a = createFunctions();
a.forEach(e=>{
    console.log(e());
})//输出0、1、2、....、8、9

重写createFunctions()函数后,每个函数会返回不同的索引值;如上代码,在闭包外定义了一个匿名函数,且传参数num,在调用内个匿名函数时传入变量i;由于函数参数是按值传递的,将变量i的当前值复制给参数num;因此函数数组中每个函数都有自己num变量的一个副本,返回各自不同的num值。

关于this对象

定义:this对象是在运行时基于函数的执行环境绑定的;
①全局函数中:this等于window;
②函数被作为对象的方法调用时:this等于那个对象;
③匿名函数中:this等于window;(匿名函数的执行环境具有全局性)
理解

var name = "Window";
var object = {
    name: "Object",
    getName: function(){
        return function(){
            return this.name;
        };
    }
};
console.log(object.getName()());//"Window"(非严格模式下)

如上代码,getName()返回一个匿名函数,匿名函数中this对象指向window,因此调用object.getName()()就会立即调用它返回的函数,返回window对象中的变量name的值“Window”;
把外部作用域中的this对象保存在一个闭包能够访问到的变量里,闭包就可以访问该对象了,如下所示。

var name = "Window";
var object = {
    name: "Object",
    getName: function(){
        var that = this;
        return function(){
            return that.name;
        };
    }
};
console.log(object.getName()());//"Object"

如上代码,在函数中声明that变量并把this对象赋值给它(此时this指向object),因此闭包可以访问这个变量that且that引用object,返回的是object的name值;通过call()或apply()改变函数执行环境也可使this指向其他对象。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。