七月 20th, 2009

一个js闭包的小例子

前端开发, by army8735.

上午看到无忧脚本里一个闭包的小讨论贴,下午就碰到blue来说一个js问题。眼不转头不抬回了句:“代码不全。”结果才发现,是我智商不全。

举个小例子:

<ul>
  <li id="a1">1</li>
  <li id="a2">2</li>
  <li id="a3">3</li>
</ul>

写个代码:

for(var i = 0; i < 4; i++) {
  var oLi = document.getElementById("a" + i);
  oLi.onclick = function() {
    alert(i);
  }
}

你会认为每个li元素分配的点击事件是弹出各自的序号吗?如果答案是“是”,那么答案就错了。最终结果会是,所有弹出的均是4,根本不是1、2、3。因为i的变量只保存了一个,每个li弹出的都是i,i最后是4,并非我们所想每个li元素会保存自己的序号。

再来看blue给的代码:

function User( properties ) {
  //遍历对象属性,确保它作用域正确(如前所述)
  for ( var i in properties ) {
    //为属性创建获取器
    this[ "get" + i ] = function() {
      return  properties[i];
    };
    //为属性创建设置器
    this[ "set" + i ]  = function(val) {
      properties[i] = val;
    };
  }
}
//创建一个新user对象实例,传入一个包含属性的对象作为种子
var user = new User({
  name:  "Bob",
  age: 44
});
//请注意name属性并不存在,因为它在properties对象中,是私有的
alert(  user.name == null );
//然而,我们能够使用用动态生成的方法getname来访问它
alert( user.getname()  == "Bob" );
//最后,我们能看到,通过新生成的动态方法设置和获取age都是可以的
//user.setage( 22  );
alert( user.getname());

最终会弹出什么?false、true和啥?当然不是Bob,会是44,这个原理和上面那个是一样的。

user.getname()中的那个i,在循环的时候确实是等于”name”,于是this["get" + i]为类本身赋予了一个getname的方法,返回name的属性值,也就是Bob。可是在循环下一个的时候,i变成了age,于是return properties[i]也变成了return properties["age"]了,这就是原因所在。

既然知道为什么了,那么有希望拯救吗?

当然有!

一种方法是将变量存到别的地方上去,比如开头的那个例子将序号存到元素的name属性上;当然技巧性的就是使用闭包了。

function User( properties ) {
  for ( var i in  properties ) {
    this[ "get" + i ] = (function(s) {
      return  function() {
        return  properties[s];
      }
    })(i);
    this[ "set" + i ] = function(val)  {
      properties[i] = val;
    };
  }
}
var user = new  User({
  name: "Bob",
  age: 44
});
alert(  user.getname());

试一试,效果如何?通过匿名函数的方法,将变量存到实参上面,如此就保存了每个变量,防止最后被替换掉。&

Back Top

回复自“一个js闭包的小例子”

  1. function User( properties ) {
    for ( var i in properties ) {
    this[ "get" + i ] = (function(s) {
    return function() {
    return properties[s];
    }
    })(i);
    this[ "set" + i ] = function(val) {
    properties[i] = val;
    };
    }
    }
    —————————————
    army老大.
    帮忙解一下惑.
    1.
    get方法里的s是什么啊.
    2.
    get方法this[get+i] = (…) (i);
    这俩个括号是干嘛的啊..- -

    恩.要是没时间详细解答的话.
    告诉我看什么资料也可以. 谢谢….

  2. 你应该没看明白的是那个匿名方法。

    (function() {

    })();

    这是个匿名方法,很常见的技巧,作用类似于下:

    function f() {

    }
    f();

    和执行了一个定义的方法情况相同,只是区别在于,匿名的方法特点是通过括号括起来,在定义后就直接执行了,并且执行后没办法再进行调用(没有方法名嘛),而这些是js解释器内部管理的,无需担心名称冲突。而且一个方法执行时会产生一个独立作用域,这就是一个牵扯到闭包的例子。

    至于闭包的确切含义,可以google之,或者看《javascript权威指南》,虽然有点类似工具书读起来太累,但还是值得整体通读的。我其实也没看完,小麦建议的。

  3. @army8735:
    这样的话我就明白了
    原来是 JS的匿名方法是通过()来声明的.
    谢谢啊.
    这样的话
    ()()
    //第一个括号是”方法名”,第二个就是参数列表了.
    嘿嘿.谢谢..

  4. 匿名方法不是通过()声明的。

    function() {
    ….
    }

    就是一个匿名方法,括号的作用是优先级而已……你去掉试试。

  5. 我想看 javascript 高级编程

  6. @army8735:

    对不起啊.对不起. 1月11日回复的 我昨天才看到. 狠狠的汗了一把.

    去掉小括号我直到现在还没有试.再瀑布汗.

    博主有对flex 或 as3 对图片操作的资料或者经验啊.

    主要功能是合成俩个图片 和 保存图片.

  7. @墨墨: as3操作图片我只做过clone而已。不过估计合成图片就是将两张图片的分别绘制clone成bitmapdata,然后拼在一起。

    至于保存,将图片数据保存成二进制post提交,后台编码就ok了,理论上来说。

  8. as那个解决了

    js 闭包也也解决了

    外部函数包含的对象无法访问内部函数的包含的对象.

    “闭包是通过在对一个函数调用的执行环境中返回一个函数对象构成的。”
    http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clScCh

    这篇的作者..正在翻译 《JavaScript高级程序设计(第2版)》..

    http://www.cn-cuckoo.com/2007/08/01/understand-javascript-closures-72.html

  1. 没有任何引用。

发表回复

Back Top