美文网首页前端基础笔记
【javascript】闭包

【javascript】闭包

作者: shanruopeng | 来源:发表于2017-11-20 15:54 被阅读2次

    闭包

    • 闭包是指有权访问另一个函数作用域中的变量的函数。
    • 创建闭包的常见方式,就是在一个函数内部创建另一个函数
    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】闭包

        本文链接:https://www.haomeiwen.com/subject/onvvvxtx.html