由浅到深理解闭包与匿名函数

来源:互联网 发布:天下三萌妹捏脸数据 编辑:程序博客网 时间:2024/05/17 06:54
闭包和匿名函数是JS中的非常重要的知识点,但很多人会把闭包跟匿名函数搞混淆,因为他俩会经常一起使用到。其实他们两个是不同的概念,闭包有可能是匿名函数,
也有可能不是,反之亦然。那么在这里我就从不同的方面分享一下我对闭包与匿名函数的理解。

一、概念
闭包:有权访问另一个函数作用域中的变量的函数。(函数的嵌套,内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕)
匿名函数:没有函数名的函数。(没有名字的函数表达式也叫匿名函数)

二、创建方式
闭包:常见的创建方式就是在一个函数内部创建另一个函数。如下:
function a(){
var i=0;
function b(){
alert(++i);
}
return b;
}
var c = a();
c();

这段代码有两个特点:
1)函数b嵌套在函数a内部;
2)函数a返回函数b。
这样在执行完var c=a()后,变量c实际上是指向了函数b,再执行c()后就会弹出一个窗口显示i的值(第一次为1)。这段代码其实就创建了一个闭包,为什么?
因为函数a外的变量c引用了函数a内的函数b,就是说:当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。

匿名函数:
第一种方式:
var double = function(x) { return 2* x; }
“=”右边的函数就是一个匿名函数,创造完毕函数后,又将该函数赋给了变量。
第二种方式:
(function(x, y){
    alert(x + y);  
})(2, 3);

这里创建了一个匿名函数(在第一个括号内),第二个括号用于调用该匿名函数,并传入参数。

三 、关于变量

JavaScript中的作用域链的机制引出了一个副作用,即闭包只能取得包含函数中任何变量的最后一个值。闭包所保存的是整个变量对象,而不是某个特殊的值。

function createFunctions(){      var result=new Array();              for (var i=0;i<10;i++){          result[i]=function(){              return i;          };      }      return result;  }  var funcs = createFunctions();  for (var i=0; i < funcs.length; i++){      document.write(funcs[i]() + "<br />");  }
createFunction()函数返回一个数组。表面上看,似乎每个函数都应该返回自己的索引值,但事实并非如此,事实上每个函数的返回值都是10.因为每个函数的作用域链(当代码在一个环境中执行时,会创建变量对象的一个作用域链来保证对执行环境有权访问的变量和函数的有序访问。作用域第一个对象始终是当前执行代码所在环境的变量对象,如果执行环境是函数,那么将其活动对象作为作用域链第一个对象,第二个对象是包含环境,下一个是包含环境的包含环境)中都包含着createFunctions()函数的活动对象,所以它们引用的都是同一个变量i。当createFunctions()函数返回后,变量i的值就是10,此时每个函数都引用着保存变量i的同一个变量对象,所以每个函数返回后都是10.
之所以会产生这样的结果,是由于闭包中引用与createFunctions()函数中同一个变量i的问题,所以我们可以使用匿名函数强制使闭包的行为符合预期
function createFunctions(){       var result=new Array();                 for (var i=0;i<10;i++){           result[i]=function(num){             return function(){               return num;           };           }(i);     }       return result;   }   var funcs = createFunctions();   for (var i=0; i < funcs.length; i++){       document.write(funcs[i]() + "<br />");   }

在重写了前面的createFunctions()函数后,每个函数就好返回各自不同的索引值了。在这里,我们没有直接把闭包赋值给数值,而是定义了一个匿名函数,并将立即执行该函数的结果赋值给数组。这里的匿名函数有一个参数num,也就是最终的函数要返回的值。在调用每个匿名函数时,我们传入了变量i。由于函数参数按值传递的,所以就会将变量i的当前值复制给参数num。而在这个匿名函数内部,有创建并返回了一个访问num的闭包。这样依赖,result数组中的每个函数都有自己num变量的一个副本,因此就可以返回各自不同的数值了。

四、关于this对象
在闭包中使用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"(在非严格模式下)

以上代码创建了一个全局变量name,有创建了一个包含那么属性的对象,这个对象还包括一个方法——getNameFunc(),它返回一个匿名函数,而匿名函数又返回this.name.由于getNameFunc()返会一个函数,因此调用object.getNameFunc()()就会立即返回调用它的函数,结果就返回一个字符串。然而,这个例子返回的字符串是“The Window”,即全局name变量的值。
但是,为什么匿名函数没有取得其包含作用域(或外部作用域)的this对象呢?
object.getNameFunc()()等价于
f()=object.getNameFunc();
f();
每个函数在调用时,其活动对象都会自动获取两个特殊的变量:this和arguments。内部函数在搜索这两个变量时,只会搜到其活动对象为止(搜到了就不会再继续搜了),因此永远不肯能访问到外部函数中的这两个变量,这里的this指的是window。
不过,把外部作用域中的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变量,而在定义闭包之后,闭包也可以访问这个变量,因为它们是我们在外部函数中特意声明的一个变量。即使在函数返回之后,this也仍然引用的object,所以调用object.getName()()就返回“My object”.

0 0
原创粉丝点击