Javascript 中的 valueOf() 与 toString()
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2485632/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
valueOf() vs. toString() in Javascript
提问by brainjam
In Javascript every object has a valueOf() and toString() method. I would have thought that the toString() method got invoked whenever a string conversion is called for, but apparently it is trumped by valueOf().
在 Javascript 中,每个对象都有一个 valueOf() 和 toString() 方法。我原以为每当调用字符串转换时都会调用 toString() 方法,但显然 valueOf() 胜过它。
For example, the code
例如,代码
var x = {toString: function() {return "foo"; },
valueOf: function() {return 42; }};
window.console.log ("x="+x);
window.console.log ("x="+x.toString());
will print
将打印
x=42
x=foo
This strikes me as backwards .. if x were a complex number, for example, I would want valueOf() to give me its magnitude, but whenever I wanted to convert to a string I would want something like "a+bi". And I wouldn't want to have to call toString() explicitly in contexts that implied a string.
这让我感到倒退.. 例如,如果 x 是一个复数,我希望 valueOf() 给我它的大小,但是每当我想转换为字符串时,我都会想要像“a+bi”这样的东西。而且我不想在暗示字符串的上下文中显式调用 toString() 。
Is this just the way it is?
这只是它的方式吗?
采纳答案by user187291
The reason why ("x="+x) gives "x=value" and not "x=tostring" is the following. When evaluating "+", javascript first collects primitive values of the operands, and then decides if addition or concatenation should be applied, based on the type of each primitive.
("x="+x) 给出 "x=value" 而不是 "x=tostring" 的原因如下。在评估“+”时,javascript 首先收集操作数的原始值,然后根据每个原始类型决定是应用加法还是串联。
So, this is how you think it works
所以,这就是你认为它的工作方式
a + b:
pa = ToPrimitive(a)
if(pa is string)
return concat(pa, ToString(b))
else
return add(pa, ToNumber(b))
and this is what actually happens
这就是实际发生的事情
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))
That is, toString is applied to the result of valueOf, not to your original object.
也就是说, toString 应用于 valueOf 的结果,而不是应用于您的原始对象。
For further reference, check out section 11.6.1 The Addition operator ( + )in the ECMAScript Language Specification.
如需进一步参考,请查看ECMAScript 语言规范中的第 11.6.1 节加法运算符 ( + )。
*When called in string context, ToPrimitive doesinvoke toString, but this is not the case here, because '+' doesn't enforce any type context.
*当在字符串上下文中调用时,ToPrimitive会调用 toString,但这里不是这种情况,因为 '+' 不强制任何类型上下文。
回答by bcherry
Here's a little more detail, before I get to the answer:
在我得到答案之前,这里有更多细节:
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"
The toStringfunction is not"trumped" by valueOfin general. The ECMAScript standard actually answers this question pretty well. Every object has a [[DefaultValue]]property, which is computed on-demand. When asking for this property, the interpreter also provides a "hint" for what sort of value it expects. If the hint is String, then toStringis used before valueOf. But, if the hint is Number, then valueOfwill be used first. Note that if only one is present, or it returns a non-primitive, it will usually call the other as the second choice.
该toString功能通常不会被“击败” valueOf。ECMAScript 标准实际上很好地回答了这个问题。每个对象都有一个[[DefaultValue]]属性,它是按需计算的。当请求这个属性时,解释器还提供了一个关于它期望什么样的值的“提示”。如果提示是String,则toString在之前使用valueOf。但是,如果提示为Number,valueOf则将首先使用。请注意,如果只有一个存在,或者它返回一个非原语,它通常会调用另一个作为第二选择。
The +operator always provides the hint Number, even if the first operand is a string value. Even though it asks xfor its Numberrepresentation, since the first operand returns a string from [[DefaultValue]], it does string concatenation.
该+运营商一直提供的提示Number,即使第一个操作数是一个字符串值。即使它要求x它的Number表示,因为第一个操作数从 返回一个字符串[[DefaultValue]],它执行字符串连接。
If you want to guarantee that toStringis called for string concatenation, use an array and the .join("")method.
如果要保证toString调用字符串连接,请使用数组和.join("")方法。
(ActionScript 3.0 slightly modifies the behavior of +, however. If either operand is a String, it will treat it as a string concatenation operator and use the hint Stringwhen it calls [[DefaultValue]]. So, in AS3, this example yields "foo, x=foo, foo=x, foo1, 43, x=foo".)
(但是,ActionScript 3.0 稍微修改了 的行为+。如果任一操作数是 a String,它会将其视为字符串连接运算符并String在调用 时使用提示[[DefaultValue]]。因此,在 AS3 中,此示例产生“foo, x=foo, foo =x, foo1, 43, x=foo"。)
回答by Ben Aston
TLDR
TLDR
Type coercion, or implicit type conversion, enables weak typing and is used throughout JavaScript. Most operators (with the notable exception of the strict equality operators ===and !==), and value checking operations (eg. if(value)...), will coerce values supplied to them, if the types of those values are not immediately compatible with the operation.
类型强制,或隐式类型转换,启用弱类型并在整个 JavaScript 中使用。大多数运算符(严格相等运算符===和!==除外)和值检查操作(例如if(value)...)将强制提供给它们的值,如果这些值的类型与操作不立即兼容。
The precise mechanism used to coerce a value depends on the expression being evaluated. In the question, the addition operatoris being used.
用于强制值的精确机制取决于被评估的表达式。在问题中,正在使用加法运算符。
The addition operator will first ensure both operands are primitives, which, in this case, involves calling the valueOfmethod. The toStringmethod is not called in this instance because the overridden valueOfmethod on object xreturns a primitive value.
加法运算符将首先确保两个操作数都是原语,在这种情况下,这涉及调用valueOf方法。toString在此实例中不调用该方法,因为valueOf对象上的重写方法x返回一个原始值。
Then, because one of the operands in the question is a string, bothoperands are converted to strings. This process uses the abstract, internal operation ToString(note: capitalized), and is distinct from the toStringmethod on the object (or its prototype chain).
然后,因为问题中的一个操作数是字符串,所以两个操作数都被转换为字符串。这个过程使用抽象的内部操作ToString(注:大写),区别于toString对象(或其原型链)上的方法。
Finally, the resulting strings are concatenated.
最后,将生成的字符串连接起来。
Details
细节
On the prototype of every constructor function object corresponding to every language type in JavaScript (ie. Number, BigInt, String, Boolean, Symbol, and Object), there are two methods: valueOfand toString.
在 JavaScript 中每种语言类型(即 Number、BigInt、String、Boolean、Symbol 和 Object)对应的每个构造函数对象的原型上,都有两个方法:valueOf和toString。
The purpose of valueOfis to retrieve the primitive value associated with an object (if it has one). If an object does not have an underlying primitive value, then the object is simply returned.
的目的valueOf是检索与对象关联的原始值(如果有的话)。如果对象没有底层原始值,则简单地返回该对象。
If valueOfis invoked against a primitive, then the primitive is auto-boxed in the normal way, and the underlying primitive value returned. Note that for strings, the underlying primitive value (ie. the value returned by valueOf) is the string representation itself.
如果valueOf针对原语调用,则原语以正常方式自动装箱,并返回底层原语值。请注意,对于字符串,底层原始值(即由 返回的值valueOf)是字符串表示本身。
The following code shows that the valueOfmethod returns the underlying primitive value from a wrapper object, and it shows how unmodified object instances that do not correspond to primitives, have no primitive value to return, so they simply return themselves.
以下代码显示该valueOf方法从包装器对象返回底层原始值,并显示与原始值不对应的未修改对象实例如何没有要返回的原始值,因此它们仅返回自身。
console.log(typeof new Boolean(true)) // 'object'
console.log(typeof new Boolean(true).valueOf()) // 'boolean'
console.log(({}).valueOf()) // {} (no primitive value to return)
The purpose of toString, on the other hand, is return a string representation of an object.
toString另一方面,的目的是返回对象的字符串表示。
For example:
例如:
console.log({}.toString()) // '[object Object]'
console.log(new Number(1).toString()) // '1'
For most operations, JavaScript will silently attempt to convert one or more operand(s) to the required type. This behavior was chosen to make JavaScript easier to use. JavaScript initially did not have exceptions, and this may have also played a role in this design decision. This kind of implicit type conversion is called type coercion, and it is the basis of JavaScript's loose (weak) type system. The complicated rules behind this behavior are intended to move the complexity of typecasting into the language itself, and out of your code.
对于大多数操作,JavaScript 会默默地尝试将一个或多个操作数转换为所需的类型。选择此行为是为了使 JavaScript 更易于使用。JavaScript最初没有异常,这可能也在这个设计决策中发挥了作用。这种隐式类型转换称为类型强制,它是 JavaScript 松散(弱)类型系统的基础。此行为背后的复杂规则旨在将类型转换的复杂性移入语言本身,并移出您的代码。
During the coercive process, there are two modes of conversion that can occur:
在强制过程中,可能发生两种转换模式:
- Conversion of an object to a primitive (which might involve a type conversion itself), and
- Direct conversion to a specific type instance, using a constructor function object of one of the primitive types (ie.
Number(),Boolean(),String()etc.)
- 将对象转换为原始类型(这可能涉及类型转换本身),以及
- 直接转换到一种特定类型的实例,使用该原语类型中的一个的构造函数对象(即
Number(),Boolean(),String()等等)
Conversion To A Primitive
转换为原始
When attempting to convert non-primitive types to primitives to be operated upon, the abstract operation ToPrimitiveis called with an optional "hint" of 'number', or 'string'. If the hint is omitted, the default hint is 'number' (unless the @@toPrimitivemethod has been overridden). If the hint is 'string', then toStringis tried first, and valueOfsecond if toStringdid not return a primitive. Else, vice-versa. The hint depends on the operation requesting the conversion.
当尝试将非原始类型转换为要操作的原始类型时,抽象操作ToPrimitive将使用可选的“数字”或“字符串”的“提示”调用。如果省略提示,则默认提示为“数字”(除非该@@toPrimitive方法已被覆盖)。如果提示是“字符串”,则toString首先尝试,valueOf如果toString没有返回原语,则第二次尝试。否则,反之亦然。提示取决于请求转换的操作。
The addition operator supplies no hint, so valueOfis tried first. The subtraction operator supplies a hint of 'number', so valueOfis tried first. The only situations I can find in the spec in which the hint is 'string' are:
加法运算符不提供任何提示,因此valueOf首先尝试。减法运算符提供了“数字”的提示,因此valueOf首先尝试。我可以在规范中找到提示为“字符串”的唯一情况是:
Object#toString- The abstract operation
ToPropertyKey, which converts an argument into a value that may be used as a property key
Object#toString- 抽象操作
ToPropertyKey,将参数转换为可用作属性键的值
Direct Type Conversion
直接类型转换
Each operator has its own rules for completing their operation. The addition operator will first use ToPrimitiveto ensure each operand is a primitive; then, if either operand is a string, it will then deliberately invoke the abstract operation ToStringon each operand, to deliver the string concatenation behavior we expect with strings. If, after the ToPrimitivestep, both operands are not strings, then arithmetic addition is performed.
每个操作员都有自己的规则来完成他们的操作。加法运算符将首先用于ToPrimitive确保每个操作数都是一个原语;然后,如果任一操作数是一个字符串,它就会故意调用ToString每个操作数上的抽象操作,以提供我们期望的字符串连接行为。如果在该ToPrimitive步骤之后,两个操作数都不是字符串,则执行算术加法。
Unlike addition, the subtraction operator does not have overloaded behavior, and so will invoke toNumericon each operand having first converted them to primitives using ToPrimitive.
与加法不同的是,减法运算符没有重载行为,因此会toNumeric在每个操作数上调用,首先使用 将它们转换为原语ToPrimitive。
So:
所以:
1 + 1 // 2
'1' + 1 // '11' Both already primitives, RHS converted to string, '1' + '1', '11'
1 + [2] // '12' [2].valueOf() returns an object, so `toString` fallback is used, 1 + String([2]), '1' + '2', 12
1 + {} // '1[object Object]' {}.valueOf() is not a primitive, so toString fallback used, String(1) + String({}), '1' + '[object Object]', '1[object Object]'
2 - {} // NaN {}.valueOf() is not a primitive, so toString fallback used => 2 - Number('[object Object]'), NaN
+'a' // NaN `ToPrimitive` passed 'number' hint), Number('a'), NaN
+'' // 0 `ToPrimitive` passed 'number' hint), Number(''), 0
+'-1' // -1 `ToPrimitive` passed 'number' hint), Number('-1'), -1
+{} // NaN `ToPrimitive` passed 'number' hint', `valueOf` returns an object, so falls back to `toString`, Number('[Object object]'), NaN
1 + 'a' // '1a' Both are primitives, one is a string, String(1) + 'a'
1 + {} // '1[object Object]' One primitive, one object, `ToPrimitive` passed no hint, meaning conversion to string will occur, one of the operands is now a string, String(1) + String({}), `1[object Object]`
[] + [] // '' Two objects, `ToPrimitive` passed no hint, String([]) + String([]), '' (empty string)
1 - 'a' // NaN Both are primitives, one is a string, `ToPrimitive` passed 'number' hint, 1-Number('a'), 1-NaN, NaN
1 - {} // NaN One primitive, one is an object, `ToPrimitive` passed 'number' hint, `valueOf` returns object, so falls back to `toString`, 1-Number([object Object]), 1-NaN, NaN
[] - [] // 0 Two objects, `ToPrimitive` passed 'number' hint => `valueOf` returns array instance, so falls back to `toString`, Number('')-Number(''), 0-0, 0
Note that the Dateintrinsic object is unique, in that it is the only intrinsic to override the default @@toPrimitivemethod, in which the default hint is presumed to be 'string' (rather than 'number'). The reason for having this, is to have Dateinstances translate to readable strings by default, instead of their numeric value, for the convenience of the programmer. You can override @@toPrimitivein your own objects using Symbol.toPrimitive.
请注意,Date内部对象是唯一的,因为它是唯一覆盖默认@@toPrimitive方法的内部对象,其中默认提示被假定为“字符串”(而不是“数字”)。这样做的原因是为了Date程序员的方便,默认情况下将实例转换为可读字符串,而不是它们的数值。您可以@@toPrimitive使用Symbol.toPrimitive.
The following grid shows the coercion results for the abstract equality operator (==) (source):
以下网格显示了抽象相等运算符 ( ==) ( source)的强制转换结果:
See also.
见也。


