ValueOf() против toString() в Javascript
В Javascript каждый объект имеет метод valueOf() и toString(). Я бы подумал, что метод toString() вызывается всякий раз, когда вызывается преобразование строки, но, по-видимому, оно сфабриковано значениемOf().
Например, код
var x = {toString: function() {return "foo"; },
valueOf: function() {return 42; }};
window.console.log ("x="+x);
window.console.log ("x="+x.toString());
напечатает
x=42
x=foo
Это поражает меня как назад. Если x было сложным числом, например, я бы хотел, чтобы valueOf() дал мне свою величину, но всякий раз, когда я хотел преобразовать в строку, я хотел бы получить что-то вроде "a + bi". И я бы не хотел, чтобы я вызывал toString() явно в контекстах, которые подразумевали строку.
Это так, как есть?
Ответы
Ответ 1
Причина, по которой ( "x =" + x) дает "x = значение", а не "x = tostring", следующая. При оценке "+" javascript сначала собирает примитивные значения операндов, а затем решает, следует ли применять добавление или конкатенацию в зависимости от типа каждого примитива.
Итак, так вы думаете, что это работает
a + b:
pa = ToPrimitive(a)
if(pa is string)
return concat(pa, ToString(b))
else
return add(pa, ToNumber(b))
и это то, что на самом деле происходит
a + b:
pa = ToPrimitive(a)
pb = ToPrimitive(b)*
if(pa is string || pb is string)
return concat(ToString(pa), ToString(pb))
else
return add(ToNumber(pa), ToNumber(pb))
То есть toString применяется к результату valueOf, а не к исходному объекту.
Подробнее см. раздел 11.6.1 Оператор добавления (+) в спецификации языка ECMAScript.
* При вызове в контексте строки ToPrimitive вызывает toString, но здесь это не так, потому что '+' не применяет какой-либо контекст типа.
Ответ 2
Вот немного более подробно, прежде чем я получу ответ:
var x = {
toString: function () { return "foo"; },
valueOf: function () { return 42; }
};
alert(x); // foo
"x=" + x; // "x=42"
x + "=x"; // "42=x"
x + "1"; // 421
x + 1; // 43
["x=", x].join(""); // "x=foo"
Функция toString
в общем случае не "сбита" с помощью valueOf
. Стандарт ECMAScript на самом деле очень хорошо отвечает на этот вопрос. Каждый объект имеет свойство [[DefaultValue]]
, которое вычисляется по требованию. При запросе этого свойства интерпретатор также предоставляет "подсказку" о том, какую ценность он ожидает. Если подсказка String
, тогда toString
используется до valueOf
. Но если подсказка Number
, тогда будет использоваться valueOf
. Обратите внимание, что если присутствует только один или он возвращает не-примитив, он обычно вызывает второй вариант.
Оператор +
всегда предоставляет подсказку Number
, даже если первый операнд является строковым значением. Несмотря на то, что он запрашивает x
для своего представления Number
, так как первый операнд возвращает строку из [[DefaultValue]]
, он выполняет конкатенацию строк.
Если вы хотите гарантировать, что toString
вызывается для конкатенации строк, используйте массив и метод .join("")
.
(ActionScript 3.0 немного изменяет поведение +
. Если любой из операндов является String
, он будет рассматривать его как оператор конкатенации строк и использовать подсказку String
, когда он вызывает [[DefaultValue]]
., в AS3 этот пример дает "foo, x = foo, foo = x, foo1, 43, x = foo".)