【javascript】闭包

闭包

  • 闭包是指有权访问另一个函数作用域中的变量的函数。
  • 创建闭包的常见方式,就是在一个函数内部创建另一个函数
function createComparisonFunction(propertyName) {
    return function(object1, object2){
    //内部函数的作用域链中包含createComparisonFunction()的作用域
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
        if (value1 < value2){
            return -1;
        } else if (value1 > value2){
            return 1;
        } else {
            return 0;
        }
    };
}
  • 当某个函数被调用时,会创建一个执行环境及相应的作用域链。然后,使用arguments 和其他命名参数的值来初始化函数的活动对象但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,……直至作为作用域链终点的全局执行环境。

  • 在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中。

//创建函数
var compareNames = createComparisonFunction("name");
//调用函数
var result = compareNames({ name: "Nicholas" }, { name: "Greg" });
//解除对匿名函数的引用(以便释放内存)
compareNames = null;
  • 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(PublicMethod),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

闭包与变量

  • 作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值
function createFunctions(){
    var result = new Array();
    for (var i=0; i < 10; i++){
        result[i] = function(){
            return i;
        };
    }
    return result;
}
//这个函数会返回一个函数数组,每个函数都返回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){
            //创建并返回了一个访问num 的闭包
            return function(){
                return num;
            };
        }(i);//将立即执行改匿名函数的结果赋给数组
    }
    return result;
}

关于this对象

  • this 对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this 等于那个对象
  • 匿名函数的执行环境具有全局性,因此其this对象通常指向window(在通过call()或apply()改变函数执行环境的情况下,this 就会指向其他对象。)
  • 但有时候由于编写闭包的方式不同,这一点可能不会那么明显.
var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        return function(){
            return this.name;
        };
    }
};
alert(object.getNameFunc()()); //"The Window"(在非严格模式下)
  • 为什么匿名函数没有取得其包含作用域(或外部作用域)的this 对象呢?
  • 每个函数在被调用时都会自动取得两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。
  • 把外部作用域中的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"
/**在定义匿名函数之前,我们把this对象赋值给了一个名叫that的变量。而在定义了闭包之后,闭包也可以访问这个变量,因为它是我们在包含函数中特意声名的一个变量。即使在函数返回之后,that 也仍然引用着object,所以调用object.getNameFunc()()就返回了"My Object"。**/

内存泄露

  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。
  • 如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁.
function assignHandler(){
    var element = document.getElementById("someElement");
    element.onclick = function(){
        alert(element.id);
    };
}
/**由于匿名函数保存了一个对assignHandler()的活动对象的引用,因此就会导致无法减少element 的引用数。只要匿名函数存在,element的引用数至少也是1,因此它所占用的内存就永远不会被回收**/
  • 解决方法是,在退出函数之前,将不使用的局部变量全部删除。
function assignHandler(){
    var element = document.getElementById("someElement");
    var id = element.id;
    element.onclick = function(){
        alert(id);
    };
    element = null;
}

模仿块级作用域

  • javaScript 没有块级作用域的概念,意味着在块语句中定义的变量,实际上是在包含
    函数中而非语句中创建的
function outputNumbers(count){
    for (var i=0; i < count; i++){
        alert(i);
    }
    alert(i); //计数
}
//即使像下面这样错误地重新声明同一个变量,也不会改变它的值。
function outputNumbers(count){
    for (var i=0; i < count; i++){
        alert(i);
    }
    var i; //重新声明变量
    alert(i); //计数
}
  • 匿名函数可以用来模仿块级作用域并避免这个问题。
//用作块级作用域(通常称为私有作用域)的匿名函数的语法
(function(){
//这里是块级作用域
})();
  • 这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。
(function(){
    var now = new Date();
    if (now.getMonth() == 0 && now.getDate() == 1){
        alert("Happy new year!");
    }
})();

/**这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函
数执行完毕,就可以立即销毁其作用域链了。**/
【javascript】闭包_第1张图片
好好学习

你可能感兴趣的:(【javascript】闭包)