将循环中的闭包分配给.prototype

时间:2011-04-10 00:49:31

标签: javascript closures

我正在开发一个Javascript库,我无法污染全局命名空间,并且必须包含一个或两个全局变量中的所有变量。我目前遇到了一个需要使用闭包的特殊情况,但我通常尝试的并没有起作用,这让我困扰了一段时间。搜索只让我得到了传统的闭包方法,这就是我经常使用的方法。

[...]
addFilters: function(filters) {
  for(filter in filters) {
    this.filters[filter] = filters[filter];
    this.Image.prototype[filter] = function() { //closures, how do they work?
      return (function(image, filter, arguments) {
        image.addQueue(filter, arguments);
      })(this, filter, arguments);
    };
  }
},
[...]

在上面的代码片段中,Image.prototype函数(和image.addQueue)没有正确捕获'filter',因此它每次都被设置为for..in迭代中的最后一个过滤器。

此处填写完整代码并突出显示相关部分: http://pastebin.com/UVFTVPkh

现场演示: http://ian0.com/code/js/ube/demo.html

2 个答案:

答案 0 :(得分:1)

你的工厂功能有点不稳定。您需要为其参数命名,并且不正确地执行自调用。如果您只使用单独的函数来生成分配给this.Image.prototype[filter]的函数,则会更加明显。

function generateProtoFunc(image, filter, arguments) {
    return function(filter, arguments) {
        image.addQueue(filter, arguments);
    };
}

// snip...

for (filter in filters) {
    this.filters[filter] = filters[filter];
    this.Image.prototype[filter] = generateProtoFunc(this, filter, arguments);
}

这是使用立即函数调用的正确方法:

for (filter in filters) {
    this.filters[filter] = filters[filter];
    this.Image.prototype[filter] = (function(image, filter, arguments) {
        return function(filter, arguments) {
            image.addQueue(filter, arguments);
        };
    })(this, filter, arguments);
}

答案 1 :(得分:0)

闭包绑定到声明它的范围,而不是声明时该范围内变量的特定值。
我在解释为​​什么你的代码不起作用。通过了解原因,你会发现有一种更简单的方法可以做到这一点 您在每个FOR循环中声明的所有函数都绑定到相同的作用域(addFilters函数作用域),因此当执行闭包时,它们从同一作用域读取变量,因此它们获得相同的值。
因此,这里的关键是将每个闭包绑定到不同的范围,这就是通过将闭包包装在匿名函数中所做的事情:即,您正在为每个闭包创建一个唯一的范围。

但是......你需要一个匿名函数调用来创建一个范围吗? 答案是否定的 您可以使用with语句以可读的方式创建范围。

以下是使用该方法的代码:

[...]
addFilters: function(filters) {
    for(filter in filters) {
        this.filters[filter] = filters[filter] ;
        with({ _filter: filter }) // with this you create a new scope with the local variable `_filter` holding the value of `filter`
            this.Image.prototype[filter] = function(image, arguments){ image.addQueue(_filter, arguments) } ; // and this closure is bound to the new scope
    }
},
[...]

变量imagearguments是闭包的参数,因此您无需将它们添加到新范围。实际执行闭包后,您将传递这些值。

此外,您不需要以不同方式命名_filter变量。您可以将其命名为filter,它只会影响范围的filter