Закрытие в Javascript с несколькими скобками

Может ли кто-нибудь объяснить, как это предупреждение функции, когда больше нет скобок параметров. Я не мог понять это четко.

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

Ответы

Ответ 1

При первом вызове функции первое значение сохраняется в sum. После этого возвращается function f(b), сохраняя предварительный результат в sum. С каждым последовательным вызовом вы выполняете функцию f - вы выполняете sum += b и снова возвращаете f. Если требуется контекст строки (например, в alert или console.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

Ответ 2

То, что нужно посмотреть, это фрагмент кода

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

Эта функция возвращает ссылку на себя, поэтому ее можно вызвать как можно больше раз. Важная вещь в том, что у него есть функция tostring, которая вызывается, и поскольку tostring определена внутри функции sum(), она имеет доступ к переменной sum и ее текущему значению значения (которое изменяется на f())

Ответ 3

alert ожидает строку. Если он не получает строку, он попытается преобразовать любой объект, который он получает (а функция - тип объекта) в один. Если объект имеет метод toString, то он будет вызываться для выполнения указанного преобразования.

Ответ 4

Функции sum и f всегда возвращают функцию f, вы можете вызывать ее бесконечно, не получая результат, отличный от функции.

Значение возвращается только перезаписанным методом toString f (который фактически возвращает число):

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

Функция alert неявно вызывает метод toString, когда он передает свои аргументы в строки.

Ответ 5

Он не работает так, как предполагалось во всех случаях... Проблема в том, что ожидается, что .toString возвращает строку, поэтому строковые методы в предоставленной реализации не будут работать, например. г. sum(2)(3).split() приведет к ошибке.

Хотя мы могли бы предположить, что результат sum() всегда будет считаться числом, он может быть недействительным в некоторых случаях и может быть трудно отлаживать, e. г. Я заметил проблему, когда я тестировал код, первоначально написанный с помощью .toString только на jsbin.com(он split на аргументе console.log внутренне, переопределяя его).

Вместо этого .toString должен выглядеть как return String(result);. Хорошо, что .toString (когда нет .valueOf или modern Symbol.toPrimitive) будет обрабатывать преобразование примитивов, поэтому код, ожидающий Number, также будет работать. Возможной проблемой здесь может быть "двойное" преобразование, вызванное этим.

Лучшим решением может быть использование пары .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;
}