【问题标题】:is this a good example of a closure?这是关闭的一个很好的例子吗?
【发布时间】:2012-06-27 09:49:39
【问题描述】:

我看到这是 example on MDN,但不明白为什么这是一件好事 (TM)。

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

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

为什么上面比下面好?

function makeSizer(size) {  
        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;  

<a href="#" id="size-12">12</a>  
<a href="#" id="size-14">14</a>  
<a href="#" id="size-16">16</a>  

【问题讨论】:

  • 我必须承认这是一个措辞不当的问题。深夜发帖会做到这一点:) 我的意思是为什么我要使用闭包而不是只做onclick = makeSizer(14);?其中 makeSizer(14) 指的​​是函数的第二个版本。我会按照 Kooilnc 的建议去做,并玩弄 jsFiddle。

标签: javascript function functional-programming closures


【解决方案1】:

在这种情况下,size 是封闭的变量。调用makeSizer(12) 返回一个“记住”12 的函数引用。这个函数引用被分配给一个元素上的点击处理程序(参见MDN 示例中的其余代码)。它说:如果您点击我,请致电(执行)size12。在单击时,正文的字体大小变为“12px”,因为从 makeSizer(12) 返回的函数引用被执行(封闭值为 12)。

如果您使用过该功能,您会立即调整字体大小。

function makeSizer(size) {  
        document.body.style.fontSize = size + 'px';  
}
var size12 = makeSizer(12);  
var size14 = makeSizer(14);  
var size16 = makeSizer(16); //=> now the font-size of the body is '16px'

想想当你这样做时在这种情况下会发生什么:

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

如果你点击#size-12会发生什么吗?

您链接到的 MDN 页面链接到 jsfiddle example。试试看!

是我很乐意留给社区中更虔诚的部分的事情。我会说这个闭包很有用,甚至可能很方便。

【讨论】:

  • 与此形成鲜明对比的是,只设置 body 的 fontSize 的 makeSizer 版本将返回未定义的值,而不是适合用作按钮的 onclick 处理程序的函数。
【解决方案2】:

它们都不是更好,但它们的行为不同。

在您按如下方式调用返回的函数之前,第一个实际上不会调整大小:

size12();

您的第二个示例首先声明了一个函数,然后调用了 3 次。您立即将大小设置为 12px,然后是 14px,最后是 16px。

【讨论】:

    【解决方案3】:

    你对“好”的标准是什么?

    在第一个示例中,您创建了一些必须调用才能完成工作的函数。尺寸是封闭的,很难看到任何好处。您可能想要这样做:

    function makeSizer(size) {
        return function() {
            document.body.style.fontSize = size + 'px';
        };
    }
    
    var setSmall = makeSizer(8);
    var setMedium = makeSizer(10);
    var setLarge = makeSizer(14);
    

    或类似的,所以现在你只需调用:

    setSmall();
    

    第二种情况的函数应该被称为setSize

    闭包的另一种用法是:

    var setSize = (function() {
      var sizes = {small: 8, medium: 10, large: 14};
    
      return function(size) {
    
        if (sizes.hasOwnProperty(size)) {
          document.body.style.fontSize = sizes[size] + 'px';
        }
      }
    }());
    
    setSize('small');
    

    它们是“好”、“坏”还是其他取决于您对它们进行分类的标准。

    【讨论】:

      【解决方案4】:

      在第一个示例中,makeSizer 返回一个函数。在第二个示例中,它不返回任何内容,并且名称具有误导性。两者完全不同。

      第一个例子中的推理是makeSizer 是一个函数工厂;它使“sizer”功能。 size12size14size16 是特定的“sizer”函数,由 makeSizer 制作。如果稍后调用其中之一,它将设置字体大小。

      在第二个示例中,您首先将字体大小设置为 12,然后设置为 14,然后设置为 16,并在每个阶段将无意义的返回值保存到变量中。

      【讨论】:

        【解决方案5】:

        实际上,您所拥有的不是闭包的示例。

        维基百科告诉我们closure 的定义是一个“函数以及该函数的非局部变量的引用环境。”

        更简单地说,它是一个“封闭”周围作用域变量的函数。

        一个很好的例子是这样的:

        public Func<int> ClosureExample()
        {
            int a = 1,
                b = 2;
        
            Func<int> closeIt = () => a + b;
            return closeIt;
        }
        

        让我们真正看看这是怎么回事。我们声明两个整数,然后使用 lambda 表达式/闭包/匿名方法(所有这些都是分配给第一类函数泛型的定义的有效名称)返回将用于执行此计算的第一类函数,在其他地方使用封闭的变量

        你可以说 closeIt 包含了整数 a 和 b;这些变量不属于闭包,但它们正在被闭包使用。

        【讨论】:

        • 闭包的 MDN 示例(我的示例中的第一个函数)一个闭包。所以我不明白你怎么说它不是。
        • public Func&lt;string&gt; Foo() { int a = 1, b = 1; return () =&gt; a == b ? "Yes!" : "No..."; } public void Bar() { Func&lt;string&gt; foo = Foo(); foo(); } 方法 Foo 返回一个引用方法中的整数 A 和 B 的 lambda 表达式;这些变量被称为“封闭”的方法。酒吧呼叫 Foo。包含这些变量的函数被返回并存储在 foo 中。当 foo 被调用时,“是的!”被退回。 foo 包含 a 和 b;当 Foo 超出范围时,那些应该是 GC'd。但是,它们被保留...至少直到 Bar() 超出范围。
        • 但是参数size来自父函数makeSizer,而匿名函数包含size。不是吗?参数是否传递给父函数或在父函数中声明但显然在匿名函数之前,应该无关紧要。在这两种情况下,变量都在父范围内。
        • 我上面展示的是,封闭并不完全是匿名方法的使用;闭包包含来自父作用域的变量。
        猜你喜欢
        • 1970-01-01
        • 2015-10-30
        • 2013-07-04
        • 1970-01-01
        • 2011-04-23
        • 2016-04-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多