码迷,mamicode.com
首页 > 其他好文 > 详细

闭包与私有域[第7章-函数表达式 笔记1]

时间:2015-05-20 20:14:55      阅读:118      评论:0      收藏:0      [点我收藏+]

标签:

闭包

看如下示例:

function createComparisonFunction(propertyName) {

  return function(object1, object2){

    var value1 = object1[propertyName];

    var value2 = object2[propertyName];

    if (value1 < value2){

     return -1;

    else if (value1 > value2){

     return 1;

   else {

    return 0;

   }

  };

}

例子中,突出的那两行代码是内部函数(一个匿名函数)中的代码,这两行代码访问了外部函数中的变量 propertyName 。即使这个内部函数被返回了,而且是在其他地方被调用了,但它仍然可以访问变量 propertyName 。之所以还能够访问这个变量,因为内部函数的作用域链中包createComparisonFunction() 的作用域。

在匿名函数从 createComparisonFunction() 中被返回后,它的作用域链被初始化为包含createComparisonFunction() 函数的活动对象和全局变量对象。这样,匿名函数就可以访问在createComparisonFunction() 中定义的所有变量。 更为重要的是, createComparisonFunction()函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。换句话说,当 createComparisonFunction() 函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然会留在内存中;直到匿名函数被销毁后, createComparisonFunction() 的活动对象才会被销毁

例如:

//创建函数

var compareNames = createComparisonFunction("name");

//调用函数

var result = compareNames({ name: "Nicholas" }, { name: "Greg" });//1

调用 compareNames() 的过程中产生的作用域链之间的关系如下图所示:

技术分享 

闭包与变量

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

示例如下:

function createFunctions(){

var result = new Array();

  for (var i=0; i < 10; i++){

   result[i] = function(){

   return i;

  };

}

  return result;

}

这个函数会返回一个函数数组。表面上看,似乎每个函数都应该返自己的索引值,但实际上,每个函数都返回 10。因为每个函数的作用域链中都保存着 createFunctions() 函数的活动对象,所以它们引用的都是同一个变量 i 。当createFunctions() 函数返回后,变量 i 的值是 10,此时每个函数都引用着保存变量 i 的同一个变量对象,所以在每个函数内部 i 的值都是 10。

可以通过创建另一个匿名函数强制让闭包的行为符合预期:

function createFunctions(){

  var result = new Array();

  for (var i=0; i < 10; i++){

    result[i] = function(num){

                 return function(){

                   return num;

                 };

              }(i);

  }

  return result;

}

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

 

 this 对象

this对象是在运行时基于函数的执行环境绑定的:在全局函数中, this 等于 window ,而当函数被作为某个对象的方法调用时, this 等于那个对象。不过,匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。但有时候由于编写闭包的方式不同,这一点可能不会那么明显。

如下示例:

 

var name = "The Window";

var object = {

  name : "My Object",

   getNameFunc : function(){

    return function(){

      return this.name;

   };

  }

};

alert(object.getNameFunc()); //"The Window"(在非严格模式下)

以上代码先创建了一个全局变量 name ,又创建了一个包含 name 属性的对象。这个对象还包含一个方法getNameFunc() , 它返回一个匿名函数, 而匿名函数又返回 this.name 。 由于 getNameFunc()返回一个函数,因此调用 object.getNameFunc() 就会立即调用它返回的函数。而每个函数在被调用时都会自动取得两个特殊变量: this 和 arguments 。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。

 

模仿块级作用域

JavaScript 没有块级作用域的概念。这意味着在块语句中定义的变量,实际上是在包含函数中而非语句中创建的。而且JavaScript从来不会告诉你是否多次声明了同一个变量,遇到这种情况,它只会对后续的声明视而不见,不过,它会执行后续声明中的变量初始化。

不过匿名函数可以用来模仿块级作用域并避免这个问题:

(function(){

//这里是块级作用域

})();

将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式。而紧随其后的另一对圆括号会立即调用这个函数。

无论在什么地方,只要临时需要一些变量,就可以使用私有作用域,例如:

function outputNumbers(count){

 (function () {

   for (var i=0; i < count; i++){

     alert(i);

   }

 })();

  alert(i); //导致一个错误!

}

在这个重写后的 outputNumbers() 函数中,我们在 for 循环外部插入了一个私有作用域。在匿名函数中定义的任何变量, 都会在执行结束时被销毁。 因此, 变量 i 只能在循环中使用, 使用后即被销毁。而在私有作用域中能够访问变量 count ,是因为这个匿名函数是一个闭包,它能够访问包含作用域中的所有变量。

 

闭包与私有域[第7章-函数表达式 笔记1]

标签:

原文地址:http://www.cnblogs.com/aries1991/p/4517950.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!