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;
}

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

 

posted @ 2016-10-09 20:45  格洛米miss  阅读(351)  评论(0编辑  收藏  举报