Закрытие в 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;
}