JavaScript 字符串插值产生的结果与字符串拼接不同

6
我是一名有用的助手,可以为您翻译文本。
我遇到了一个情况,其中JavaScript字符串插值与字符串连接不会产生相同的结果。
以下是显示差异的简化代码版本:
const mmt = moment();
console.log('concatenated: ' + mmt); // "concatenated: 1651070909974"
console.log(`interpolated: ${mmt}`); // "interpolated: Wed Apr 27 2022 10:48:29 GMT-0400"
console.log('mmt.valueOf(): ' + mmt.valueOf()); // "mmt.valueOf(): 1651070909974"
console.log('mmt.toString(): ' + mmt.toString()); // "mmt.toString(): Wed Apr 27 2022 10:48:29 GMT-0400"

所以我立即想到这是由于 .toString().valueOf() 的差异造成的,因此我制作了一个小测试对象来验证:

const obj = {
  toString: () => 'toString',
  valueOf: () => 'valueOf',
};

console.log('concatenated: ' + obj); // "concatenated: valueOf"
console.log(`interpolated: ${obj}`); // "interpolated: toString"
console.log('obj.valueOf(): ' + obj.valueOf()); // "obj.valueOf(): valueOf"
console.log('obj.toString(): ' + obj.toString()); // "obj.toString(): toString"

然而,当我尝试使用 Date 对象进行此操作时(它的 .toString().valueOf() 的结果也不同),我并没有得到相同的行为——这次插值和连接都使用了 .toString() 的值:
const dte = new Date();
console.log('concatenated: ' + dte); // "concatenated: Wed Apr 27 2022 10:48:29 GMT-0400 (Eastern Daylight Time)"
console.log(`interpolated: ${dte}`); // "interpolated: Wed Apr 27 2022 10:48:29 GMT-0400 (Eastern Daylight Time)"
console.log('dte.valueOf(): ' + dte.valueOf()); // "dte.valueOf(): 1651070909974"
console.log('dte.toString(): ' + dte.toString()); // "dte.toString(): Wed Apr 27 2022 10:48:29 GMT-0400 (Eastern Daylight Time)"

我的问题是:当使用串联和插值时,插值的值如何转换为字符串的实际规则是什么,为什么Date对象似乎与其他对象不同?(我尝试查找了解这个问题,但目前为止我的搜索没有成功...) JSFiddle示例
1个回答

5
行为上的差异确实与 + 操作符有关,它背后有一个特定的过程:
ECMAScript 规范中的抽象操作ToPrimitive规定,如果没有提供类型提示(就像+操作符),则会发生以下情况:
  • 如果对象具有Symbol.toPrimitive方法,则将调用该方法(带有提示"default")。此方法可能将调用toString。这是Date对象的情况。
  • 如果对象没有这种方法,则默认值为 "number",并将调用valueOf。这是 moment 对象的情况。
处理 + 操作符时采用复杂流程的原因是它还可以用于算术加法。
在评估模板字符串时,不需要执行这种复杂性流程,因为始终意味着进行字符串连接,所以会使用 "string" 提示来调用 Symbol.toPrimitive 方法(而不是 "default" 提示),或者如果不存在该方法,则将调用 toString 方法。
因此,您认为 + 只是纯粹的字符串连接的做法并不准确。请看当您使用 .concat 方法时,它是如何不同的:

const mmt = moment();
console.log('concatenated: '.concat(mmt));
// Not same result as with +
console.log('plus operator: ' + mmt);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.3/moment.min.js"></script>


谢谢!我以前从未听说过.toPrimitive,所以我的所有搜索都没有找到它。 - Kip
@Kip,应该是[Symbol.toPrimitive],而不是.toPrimitive - Mulan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接