【问题标题】:Doubts About Use of Practical Closure对使用实际关闭的怀疑
【发布时间】:2012-04-27 09:35:09
【问题描述】:

我正在尝试了解更多关于 Javascript 中的闭包的信息,并且正在经历这个:https://developer.mozilla.org/en/JavaScript/Guide/Closures#Practical_closures

根据这篇文章,通过使用这样的功能:

function makeSizer(size) {  
    return function() {  
        document.body.style.fontSize = size + 'px';  
    };  
}  

var size12 = makeSizer(12);  
var size14 = makeSizer(14);  
var size16 = makeSizer(16); 

然后我们可以使用这些语句来增加/减少页面上文本的字体大小:

document.getElementById('size-12').onclick = size12;  
document.getElementById('size-14').onclick = size14; 
document.getElementById('size-16').onclick = size16;

虽然我理解了这里的概念——即 size12、size14 和 size16 成为允许访问内部函数的闭包,但我不禁觉得这是不必要的。拥有不是更容易:

function makeSizer(size) {  
    document.body.style.fontSize = size + 'px';  
}   

,然后用这些调用它?

document.getElementById('size-12').onclick = makeSizer(12);  
document.getElementById('size-14').onclick = makeSizer(14); 
document.getElementById('size-16').onclick = makeSizer(16);

谁能告诉我我的想法是否正确 - 或者我只是 Javascript 的新手,不了解在这种情况下使用闭包的好处,在这种情况下,如果你能解释一下,我会很高兴这样做的好处。

提前谢谢各位。

【问题讨论】:

  • @Joseph:不,这是一个完全有效的闭包用法。

标签: javascript closures


【解决方案1】:

不,你不能那样做。

好像你写过:

document.getElementById('size-12').onclick = (function(size) {  
    document.body.style.fontSize = size + 'px';  
})(12);

函数被立即调用,样式将被立即应用,并且没有注册.onclick处理程序,因为函数的返回值为undefined。

该示例的真正意义在于表明您可以从另一个函数返回一个函数,然后您可以将该结果分配给事件处理程序。


如果您未修改 makeSizer(),那么您可以按照建议分配处理程序,而无需中间变量,即:

document.getElementById('size-12').onclick = makeSizer(12);

但如果您按照您描述的方式更改makeSizer(),那不会起作用。

如果你多次使用同一个sizer,它也比将“sizer”存储在一个变量中效率低。

【讨论】:

  • 嗨@Alnitak,感谢您的明确解释。我现在明白你的意思了。我不明白的一件事 - 为什么将“sizer”存储在变量中比每次需要时调用 makeSizer 函数更有效?在后一种情况下,您使用的是已经定义的函数,而在前一种情况下,您必须声明新变量,然后调用该函数。至少对我来说,使用变量似乎更麻烦。
  • @anthonyteo 因为每次你调用makeSizer(n) 你实际上是在返回一个新的(函数)对象。
  • 所以我收集一个新函数对象的成本比一个新变量在效率方面的成本更高,对吧?
  • @anthonyteo 每个函数都需要创建一个“范围”,并且它本身也是一个对象。变量大部分只是解析器使用的东西——一旦代码被解析,它们基本上是免费的,但对象创建有一个运行时间的惩罚。
  • 我实际上并没有完全理解你上面所说的一切,但是当我对JS更熟悉时,我一定会再次回到你的评论中。无论如何,非常感谢您的帮助。干杯。
【解决方案2】:

对于您提供的示例,当然不需要关闭,但我想这只是为了简化概念。在某些情况下,虽然闭包是最好的解决方案:考虑如何在 javascript 中实现“私有”属性,或者何时需要 curryng 封装参数(即,用于回调函数)。

我希望以下示例对您有所帮助:

var makeSequencer = function() {
    var _count = 0; // not accessible outside this function
    var sequencer = function () {
        return _count++;
    }
    return sequencer;
}

var fnext = makeSequencer();
var v0 = fnext();     // v0 = 0;
var v1 = fnext();     // v1 = 1;
var vz = fnext._count // vz = undefined

【讨论】:

  • 闭包在这里是非常合理和有效的使用,因为它绑定提供的参数,尽管相同的函数可以用于不同的值。
  • 好吧,我想我明白你的意思了:所以 _count 是一个私有变量,只能由序列器函数访问(如 var v0 = fnext(); 所示)。您认为使用var v0=makeSequencer()(); 从而跳过fnext 是否可取?
  • @anthonyteo:这没有意义;您可以将makeSequencer 函数视为一个工厂,它在每次调用时都会创建并返回一个新函数(定序器本身)。
  • 对于像我这样的 javascript 新手来说,也许闭包还是太高级了?我似乎无法理解为什么我们要让一个函数返回一个函数,然后设置一个单独的变量作为这个返回的函数,并使用这个单独的变量来调用这个返回的函数。对我来说,这似乎是一项可怕的工作。虽然我确实有点理解这部分关于私有方法的基本原理:mzl.la/InJNwT
  • 闭包有时被认为是一个复杂的概念,但我认为这只是另一个概念,它可能非常有用。不要放弃小蚂蚱:)
【解决方案3】:

是的,这些变量 (sizeN) 是不必要的。可以直接将makeSizer()的结果赋值为handlers,这样看起来好多了。

但是,这些变量的使用并不是闭包的概念。这个例子中的闭包是函数makeSizer,它返回一个函数(即使没有参数),它仍然可以访问size变量。

不过,你需要看看两者之间的区别

function makeSizer(size) {  
    return function resize() {  
        document.body.style.fontSize = size + 'px';  
    };  
}

和

function resize(size) {  
    document.body.style.fontSize = size + 'px';  
}

执行makeSizer(5) 不执行任何操作,它返回一个函数,在调用时将大小设置为预定义的大小。相反,执行 resize(5) 会直接设置大小。您不能将后一个函数的结果用作事件处理程序。

【讨论】:

  • 嗨@Bergi,感谢您的解释。 Alnitak 还提到了处理程序 - 遗憾的是,我对处理程序的概念相当模糊,所以我不确定你们的意思。特别是,当您真正想要的只是相应地调整文本大小时,为什么需要注册 onclick 处理程序?
  • 您可能想阅读此introduction into JS events。如果您想直接调整文本大小(现在,在运行时),请执行该函数。但是,如果您希望在用户单击某个元素时发生这种情况,则需要在该事件发生时执行该函数。
  • 啊 - 我想我明白你的意思了。这就像在响应之前等待用户采取行动。
猜你喜欢
  • 2013-01-24
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 2017-06-21
  • 1970-01-01
  • 2018-10-18
  • 1970-01-01
相关资源
最近更新 更多