在标准 JavaScript ES6 环境中,.toString() 什么时候被调用过?
Within the standard JavaScript ES6 environment, when is .toString() ever called?
我才发现调用.toString()
的时候是字符串拼接和字符串插值:
// Inside of Node:
> let arr = [1,3,5];
> arr.toString()
'1,3,5'
> "" + arr
'1,3,5'
> arr + ""
'1,3,5'
> `${arr}`
'1,3,5'
据推测,console.log
打印出对象的字符串表示,应该使用toString()
,但我不确定它是否toString()
通常没有正确实现,所以console.log
做其他事情:
> console.log(arr);
[ 1, 3, 5 ]
> console.log("arr is %s", arr);
arr is [ 1, 3, 5 ]
那么在 JavaScript 本身内部,什么时候调用过 toString()
?
我认为通过多态性,我们自己编写的任何东西,我们都可以使用 ourObj.toString()
来获得我们对象的字符串表示形式作为字符串。但我想知道 JavaScript 本身(它的所有功能、库、类),什么时候真正调用 toString()
?
在 EcmaScript 语言规范的几个部分中,提到了 toString
。一个重要的用途出现在抽象操作 OrdinaryToPrimitive 中:这个函数将寻找对象的 toString
或 valueOf
方法,并执行它。优先级会受到 hint 参数的影响。
反过来,OrdinaryToPrimitive
被抽象操作调用ToPrimitive
ToPrimitive
由 ToNumber
、ToString
、ToPropertyKey
、关系比较、相等比较、表达式求值、Date
构造函数、几个字符串化方法,如 toJSON
、...等
事实上,该语言充满了将要执行的内部操作 ToPrimitive
。该规范有 200 多次引用 ToString
.
例子
这里是一个带有toString
方法实现的对象,目的是为了证明toString
是内部调用的
然后跟随几个每个触发的表达式toString
。
// Preparation
let obj = {
toString() {
this.i = (this.i||0) + 1; // counter
console.log("call #" + this.i);
return "0";
}
};
// Trigger toString via several constructs
({})[obj];
1 < obj;
1 == obj;
1 + obj;
1 - obj;
+obj;
Math.abs(obj);
parseInt(obj);
new Date(obj);
new RegExp(obj);
new Number(obj);
Symbol(obj);
"".localeCompare(obj);
[obj, null].sort();
[obj].join();
`${obj}`;
setTimeout(obj); // Not standard EcmaScript environment, but defined in the agent.
如果在 obj
上定义了 valueOf
方法,其中一些将不会触发 toString()
,这将 return 一个原始值。
举例说明一些可能更常见的情况:
- 字符串插值(可能最有用)
- 字符串添加或连接
- 符号或字符串的创建
- 作为 属性 键
- 被
Array
的join()
使用
节点控制台内部:
> let foo = { a: 123, toString: function() { return `an object with value ${this.a}`; } };
> foo
{ a: 123, toString: [Function: toString] }
> foo.toString()
'an object with value 123'
> foo.a = 456;
456
> foo.toString()
'an object with value 456'
> `${foo}`
'an object with value 456'
> "foo: " + foo
'foo: an object with value 456'
> "foo: ".concat(foo)
'foo: an object with value 456'
> let d = {};
> d[foo] = "hello";
'hello'
> d
{ 'an object with value 456': 'hello' }
> Symbol(foo)
Symbol(an object with value 456)
> String(foo)
'an object with value 456'
> let bar = Object.assign({}, foo); // clone
> bar.a = 789;
789
> [foo, bar].join(" - ")
'an object with value 456 - an object with value 789'
我才发现调用.toString()
的时候是字符串拼接和字符串插值:
// Inside of Node:
> let arr = [1,3,5];
> arr.toString()
'1,3,5'
> "" + arr
'1,3,5'
> arr + ""
'1,3,5'
> `${arr}`
'1,3,5'
据推测,console.log
打印出对象的字符串表示,应该使用toString()
,但我不确定它是否toString()
通常没有正确实现,所以console.log
做其他事情:
> console.log(arr);
[ 1, 3, 5 ]
> console.log("arr is %s", arr);
arr is [ 1, 3, 5 ]
那么在 JavaScript 本身内部,什么时候调用过 toString()
?
我认为通过多态性,我们自己编写的任何东西,我们都可以使用 ourObj.toString()
来获得我们对象的字符串表示形式作为字符串。但我想知道 JavaScript 本身(它的所有功能、库、类),什么时候真正调用 toString()
?
在 EcmaScript 语言规范的几个部分中,提到了 toString
。一个重要的用途出现在抽象操作 OrdinaryToPrimitive 中:这个函数将寻找对象的 toString
或 valueOf
方法,并执行它。优先级会受到 hint 参数的影响。
反过来,OrdinaryToPrimitive
被抽象操作调用ToPrimitive
ToPrimitive
由 ToNumber
、ToString
、ToPropertyKey
、关系比较、相等比较、表达式求值、Date
构造函数、几个字符串化方法,如 toJSON
、...等
事实上,该语言充满了将要执行的内部操作 ToPrimitive
。该规范有 200 多次引用 ToString
.
例子
这里是一个带有toString
方法实现的对象,目的是为了证明toString
是内部调用的
然后跟随几个每个触发的表达式toString
。
// Preparation
let obj = {
toString() {
this.i = (this.i||0) + 1; // counter
console.log("call #" + this.i);
return "0";
}
};
// Trigger toString via several constructs
({})[obj];
1 < obj;
1 == obj;
1 + obj;
1 - obj;
+obj;
Math.abs(obj);
parseInt(obj);
new Date(obj);
new RegExp(obj);
new Number(obj);
Symbol(obj);
"".localeCompare(obj);
[obj, null].sort();
[obj].join();
`${obj}`;
setTimeout(obj); // Not standard EcmaScript environment, but defined in the agent.
如果在 obj
上定义了 valueOf
方法,其中一些将不会触发 toString()
,这将 return 一个原始值。
举例说明一些可能更常见的情况:
- 字符串插值(可能最有用)
- 字符串添加或连接
- 符号或字符串的创建
- 作为 属性 键
- 被
Array
的join()
使用
节点控制台内部:
> let foo = { a: 123, toString: function() { return `an object with value ${this.a}`; } };
> foo
{ a: 123, toString: [Function: toString] }
> foo.toString()
'an object with value 123'
> foo.a = 456;
456
> foo.toString()
'an object with value 456'
> `${foo}`
'an object with value 456'
> "foo: " + foo
'foo: an object with value 456'
> "foo: ".concat(foo)
'foo: an object with value 456'
> let d = {};
> d[foo] = "hello";
'hello'
> d
{ 'an object with value 456': 'hello' }
> Symbol(foo)
Symbol(an object with value 456)
> String(foo)
'an object with value 456'
> let bar = Object.assign({}, foo); // clone
> bar.a = 789;
789
> [foo, bar].join(" - ")
'an object with value 456 - an object with value 789'