JavaScript闭包
闭包:
指有权访问另一个函数作用域中的变量的函数。
创建闭包的常见方式,就是在一个函数内部创建另一个函数。
1 function createComparisonFunction(propertyName){ 2 return function(obj1,obj2){//匿名函数 3 var value1 = obj1[propertyName]; 4 var value2 = obj2[propertyName]; 5 if(value1<value2){ 6 return -1; 7 }else if(value1>value2){ 8 return 1; 9 }else{ 10 return 0; 11 } 12 }; 13 }
作用域链:
当某个函数第一次被调用时,会创建一个执行环境及相应的作用域链,并把作用域链赋值给一个特殊的内部属性。然后,使用this、arguments和其他命名参数的值来初始化函数的活动对象。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象始终处于第三位,。。。直至作为作用域链终点的全局执行环境。
1 function compare(value1,value2){ 2 if(value1<value2){ 3 return -1; 4 }else if(value1>value2){ 5 return 1; 6 }else{ 7 return 0; 8 } 9 } 10 11 var result = compare(5,10);
第一次调用compare()时,会创建一个包含this,arguments,value1和value2的活动对象。全局执行环境的变量对象(包括this,result和compare)在compare()执行环境的作用域链中处于第二位。
一般来说,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域。但是,闭包的情况又有所不同。
在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中。在外部函数中定义的匿名函数会将外部函数的活动对象添加到它[指匿名函数]的作用域链中)
1 var compare = createComparisonFunction('name'); 2 var result = compare({name:'Nicholas'},{name:'Greg'});
当匿名函数从createComparisonFunction()中被返回后,它的作用域链被初始化为包含createComparisonFunction()函数的活动对象和全局变量对象。这样,匿名函数就可以访问在createComparisonFunction()中定义的所有变量。createComparisonFunction()函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数仍然在引用这个活动对象。
就是说,当createComparisonFunction()函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然会留在内存中;直到匿名函数被销毁后,createComparisonFunction()的活动对象才会被销毁。例如,
1 var compare = createComparisonFunction('name'); 2 var result = compare({name:'Nicholas'},{name:'Greg'}); 3 compare = null;//解除对匿名函数的引用,以便释放内存。
this对象:
this对象是在运行时基于函数的执行环境绑定的--在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。
1 var name="the window"; 2 var object = { 3 name:"My Object", 4 getNameFunc:function(){ 5 return function(){ 6 return this.name; 7 }; 8 } 9 }; 10 alert(object.getNameFunc()());//the window(非严格模式下)
object.getNameFunc()返回一个函数,object.getNameFunc()()调用返回的函数,调用对象默认为window对象。
把外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了。
1 var name="the window"; 2 var object = { 3 name:"My Object", 4 getNameFunc:function(){ 5 var that = this; 6 return function(){ 7 return that.name; 8 }; 9 } 10 }; 11 alert(object.getNameFunc()());//My Object
定义闭包之后,闭包可以访问that变量,即使外部函数返回之后,that仍然引用这object,所以object.getNameFunc()()返回的是My Object.
特殊情况,
1 var name="the window"; 2 var object = { 3 name:"My Object", 4 getName:function(){ 5 return this.name; 6 } 7 }; 8 object.getName();//'My Object' 9 (object.getName)();//'My Object',this的值不变 object.getName = (object.getName) 10 (object.getName=object.getName)();//'the window',非严格模式下.赋值表达式的值是函数本身,this的值不能得到维持
内存泄露:
如果闭包的作用域链中保存着一个html元素,那么就意味着该元素将无法被销毁。
1 function assignHandler(){ 2 var element = document.getElementById("someElement"); 3 element.onclick = function(){//闭包函数 4 alert(element.id);//循环引用,只要匿名函数存在,element的引用书至少是1,它所占用的内存就永远不会被回收。 5 }; 6 }
修改,
1 function assignHandler(){ 2 var element = document.getElementById("someElement"); 3 var id = element.id; 4 element.onclick = function(){//闭包函数 5 alert(id);//消除循环引用,但element仍然被间接引用着 6 }; 7 element = null;//接触对element的引用,回收其占用的内存 8 }