【问题标题】:Closure in Javascript with multiple brackets在 Javascript 中用多个括号关闭
【发布时间】:2025-12-18 15:00:01
【问题描述】:

任何人都可以解释这个函数如何在传递更多参数时发出警报。看不懂。

function sum(a) {

  var sum = a

  function f(b) {
    sum += b
    return f
  }

  f.toString = function() { return sum }

  return f
}

alert( sum(1)(2) )  // 3
alert( sum(5)(-1)(2) )  // 6
alert( sum(6)(-1)(-2)(-3) )  // 0
alert( sum(0)(1)(2)(3)(4)(5) )  // 15

【问题讨论】:

    标签: javascript closures


    【解决方案1】:

    alert 需要一个字符串。如果它没有得到一个字符串,它将尝试将它接收到的任何对象(并且函数是一种对象)转换为一个对象。如果对象具有toString 方法,则将调用该方法来执行所述转换。

    【讨论】:

    • 所有对象都有一个toString 函数,只是在大多数情况下它是默认的“[object Constructorname]”格式。在这种情况下,该方法被覆盖以产生结果。
    【解决方案2】:

    要看的是这段代码

    function f(b) {
        sum += b
        return f
      }
    

    此函数返回对自身的引用,因此可以尽可能多次地调用它。关于它的重要一点是它有一个 tostring 函数,因为 tostring 是在函数 sum() 内部定义的,所以它可以访问变量 sum 及其值当前值(由 f() 更改)

    【讨论】:

      【解决方案3】:

      sumf 函数总是返回 f 函数,您可以无限次调用它而不会得到函数以外的结果。

      该值仅由f 的覆盖toString 方法返回(实际上返回一个数字):

      console.log( sum(1)(2) ) // Function(){}
      console.log( sum(1)(2).toString() ) // 3
      

      alert 函数在将其参数转换为字符串时隐式调用 toString 方法。

      【讨论】:

        【解决方案4】:

        第一次调用您的函数时,第一个值存储在sum 中。之后function f(b) 将被返回,在sum 中保持临时结果。每次连续调用时,您都会执行函数 f - 您执行 sum += b 并再次返回 f。如果需要字符串上下文(例如在alertconsole.log 中),则改为调用f.toString,返回结果(sum)。

        function sum(a) {
        
          var sum = a
        
          function f(b) {
            sum += b
            return f  //<- from second call, f is returned each time
                      //   so you can chain those calls indefinitely
                      //   function sum basically got "overridden" by f
          }
        
          f.toString = function() { return sum }
        
          return f //<- after first call, f is returned
        }
        

        解释:

        alert( sum(6)(-1)(-2)(-3) )  // 0
                   /\ function sum called, f returned
                      /\ the returned function f is called, f returns itself
                          /\ again
                             /\ and again
                                 /\ at last, alert() requires string context,
                                    so f.toString is getting invoked now instead of f
        

        【讨论】:

        • 基本上在第一个警报 sum(1)(2) 中是 a & b 而在第二个警报中是 sum(6)(-1)(2) 现在函数如何理解 ( 2) 也是 f(b) 论点。
        • @Babu 摆脱对a和b的思考。你可以想到sum()被调用一次,初始化var sum,然后在第一次调用后被覆盖,通过调用f(b)处理每个连续的调用(因为f本身每次都返回自己,因此可以做进一步的事情链接)函数,除非出现字符串上下文。现在更清楚了吗?
        • 很好的解释。你能告诉我而不是 f.toString 你能告诉我直接返回总和的代码吗? function sum(a) { var sum = a function f(b) { sum += b return sum } // f.toString = function() { return sum } return f } sum(1)(2);
        • @BabuArumugam:关键是你不能……你为什么需要这个?
        【解决方案5】:

        在所有情况下它都不能按预期工作......问题是.toString 应该返回一个字符串,所以提供的实现中的字符串方法不起作用,例如。 G。 sum(2)(3).split() 会报错。

        虽然我们可能假设sum() 的结果总是被期望为一个数字,但在某些情况下它可能不是真的并且可能难以调试,例如。 G。我在测试最初仅在 jsbin.com 上使用 .toString 编写的代码时注意到了这个问题(它在内部对 console.log 参数执行 split,覆盖它)。

        相反,.toString 应该看起来像 return String(result);.toString(当没有 .valueOf 或现代 Symbol.toPrimitive 时)将处理基元转换,因此期望数字的代码也可以工作。这里可能的问题可能是由此引起的“双重”转换。

        如果您只针对现代浏览器,更好的解决方案可能是使用一对.toString.valueOf,或者只使用一个Symbol.toPrimitive

        使用Symbol.toPrimitive的示例:

        function sum(a) {
          let result = a;
        
          function f(b) {
            result += b;
        
            return f;
          }
        
          f[Symbol.toPrimitive] = hint => hint === 'string' ? String(result) : result;
        
          return f;
        }
        

        使用.toString.valueOf 对的示例。

        function sum(a) {
          var result = a;
        
          function f(b) {
            result += b;
        
            return f;
          }
        
          // avoiding double conversion which will happen in case of .toString
          f.valueOf = function() { return result; };
          f.toString = function() { return String(result); };
        
          return f;
        }
        

        【讨论】: