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; }
适合场合:单例必须是某种类型的实例,并且还必须添加某些属性或方法对其加以增强。