zoukankan      html  css  js  c++  java
  • js中的闭包

    闭包:在一个函数内部创建另一个函数,另一个函数有权访问这个函数的局部变量。

    function comparison(propertyName){//使数组可以比较字符串也可以比较number排序
          return function(obj1,obj2){
               var value1=obj1[propertyName];//可以访问到外部函数的变量propertyName
               var value2=obj2[propertyName];
               if(value1<value2){
                     return -1;
               }else if(value1>value2){
                     return 1;
               }else{
                     return 0;
               }
          }
    }
    var person=[{name:'a',age:12},{name:'b',age:8}];
    person.sort(comparison('name'));
    alert(person[0]).name);//a
    person.sort(comparison('age'));
    alert(person[0]).name);//b

    在这里,执行comparison(propertyName)则返回的是另一个函数function(obj1,obj2){...}。

    每个执行环境都有一个变量对象,全局环境的变量始终存在,而函数中的局部环境的变量对象则只在函数执行过程中存在。

    过程如下:

    创建函数时会先创建一个包含全局变量对象的作用域链(指向变量对象的指针列表),保存在内部[[Scope]]属性,当调用函数的时候,会为函数创建一个执行环境,复制[[Scope]]属性中的作用域链。然后活动对象会被创建并推入作用域链前端。当函数内部还定义了一个函数,内部函数会将外部函数的活动对象添加到它的作用域链。所以即便外部函数执行完毕,其活动对象也不会被销毁,因为内部函数的作用域链依然在引用这个活动对象,所以若内部函数不执行,它的活动对象将会一直留在内存中。

    为了防止占用内存,我们应该及时解除外部函数对内部函数的引用。

    var person=[{name:'a',age:12},{name:'b',age:8}];
    var comparison=comparison('name')
    person.sort(comparison);
    comparison=null;//释放内存

    还需要注意的一点是:闭包只能取得外部函数中任何变量的最后一个值。

    function create(){
        var result=new Array();
        for(var i=0;i<10;i++){
            result[i]=function(){
                return i;
            }
        }
        return result;
    }
    var a=create();
    for(var j=0;i<10;j++){
        alert(a[i]());//10,10,10...
    }

    当create函数返回后,变量已经是最后一个变量i了,所以内部函数执行时弹出的都是最后一个i,那我们要如何解决这个问题呢?

    ①内部匿名函数自执行

    function create(){
        var result=new Array();
        for(var i=0;i<10;i++){
            result[i]=(function(num){
                return num;
            })(i);
        }
        return result;
    }
    var a=create();
    for(var j=0;i<10;j++){
        alert(a[i];//0,1,2...

    直接让匿名函数自执行,此时result中存的是内部函数执行的结果。

    ②闭包

    function create(){
        var result=new Array();
        for(var i=0;i<10;i++){
            result[i]=function(num){
                return function(){
                    return num;
                }
            }(i);
        }
        return result;
    }
    var a=create();
    for(var j=0;i<10;j++){
        alert(a[i];//0,1,2...

    result返回内部匿名函数,将变量i作为参数num传入内部匿名函数,内部匿名函数就可以取到i的当前值了。

    但闭包中的this对象会存在一些问题。

    一般来说:

    ①this永远指向函数运行时所在的对象,而不是函数创建时所在的对象,不处于任何对象中的函数指向window(被谁调用,this指向谁)。

    ②call,apply,with指定的this是谁就是谁。

    而在匿名函数中,它的执行环境具有全局性,所以他的this通畅指向window。

    var name='a';
    var obj={ 
        name:'b',
        getName:function(){
            return function(){
                return this.name;
            }
        }
    }
    alert(obj.getName()());//a

    每个函数在被调用时都会自动取得两个特殊变量:this,arguments。但是内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此它永远不能直接访问外部函数中的这两个变量,那我们要怎么访问到this呢?

    ①保存this

    var name='a';
    var obj={ 
        name:'b',
        getName:function(){
            var that=this;//保存this对象
            return function(){
                return that.name;
            }
        }
    }
    alert(obj.getName()());//b

    ②对象冒充

    var name='a';
    var obj={ 
        name:'b',
        getName:function(){
            return function(){
                return this.name;
            }
        }
    }
    alert(obj.getName().call(obj));//b

    ③自调用

    var name='a';
    var obj={ 
        name:'b',
        getName:(function(){
            return function(){
                return this.name;
            }
        })();
    }
    alert(obj.getName()());//b

    注意:function若是当作一个函数声明的开始,则函数声明后面不能跟圆括号(function(){}() 是错的)。只能在函数表达式后面跟圆括号,将函数声明转换成函数表达式((function(){})())。

    私有变量:在函数中定义的变量。

    特权方法:有权访问私有变量和私有函数的共有方法。

    function MyObject(){
        var a=10;
        function privateFunction(){
            return false;
        }
        this.publicMethod=function(){//特权方法
            a++;
            return privateFunction();
        }
    }

    这样子就可以隐藏那些不应该被直接修改的数据。

    静态私有变量:被所有对象共享的变量。

    (function(){
        var name="";
         MyObject=function(value){name=value;};//全局构造函数,外部也可使用(严格模式下出错)
         MyObject.prototype.getName=function(){//特权方法
            return name;
         }
        MyObject.prototype.setName=function(value){//特权方法
            name=value;
         }
    })();
    var p1=new MyObject('a');
    alert(p1.getName());//a
    var p1=new MyObject('b');
    alert(p1.getName());//b
    alert(p2.getName());//b

    在这个方法中,变量name就变成了一个静态私有变量,能被所有实例共享,但不能直接p1.name获取。

    模块模式:为单例(只有一个实例的对象)创建私有变量和特权方法。

    var a=function(){
        var components=new Array();//私有变量
        components.push(new BaseComponent());//初始化
        return {//公共
            getCount:function(){return components.length;},
            registerComponent:function(component){
                if(typeof component=="object"){
                    components.push(component);
                }
            }
        }
    }

    这个对象字面量定义了一个单例的公共接口,它的使用场合:创建一个对象并以某些数据对其初始化,同时公开一些能够访问这些数据的方法。

    增强模块模式:

    var a=function(){
        var components=new Array();//私有变量
        components.push(new BaseComponent());//初始化
        var app=new  BaseComponent();
        //公共
        app.getCount:function(){return components.length;},
        app.registerComponent:function(component){
             if(typeof component=="object"){
                  components.push(component);
             }
        }
        return app;
    }

    适合场合:单例必须是某种类型的实例,并且还必须添加某些属性或方法对其加以增强。

  • 相关阅读:
    js与设计模式访问者模式
    js与设计模式外观模式
    由一个小Bug推及ie及ff的dom元素差异
    构建一个前端库做一个富客户端的基类
    [原创]LINQ 学习系列教程文章索引
    Sublime Text 2 性感无比的代码编辑器!程序员必备神器!跨平台支持Win/Mac/Linux
    Ubuntu分区
    非常不错的WCF入门文章,来自Artech
    助记:MIME类型
    F#学习存疑求解答:关于使用Cotinuation仍然堆栈溢出的问题
  • 原文地址:https://www.cnblogs.com/gromimiss/p/5943716.html
Copyright © 2011-2022 走看看