Javascript 字符串插值给出的结果与字符串连接不同

Javascript string interpolation gives different result than string concatenation

我 运行 遇到 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() 不同的结果)时,我 not 得到相同的行为——这次插值和连接都使用 .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 Example

行为上的差异确实与 + 运算符有关,它背后有一个特定的过程:

关于抽象操作的 ECMAScript 规范ToPrimitive specifies that if no type hint is provided (as is the case with the + operator) 发生以下情况:

  • 如果对象有一个 Symbol.toPrimitive 方法,那么它将被调用(提示“默认”)。此方法可以将调用转发给 toStringDate 个对象就是这种情况。
  • 如果对象没有这样的方法,“number”是默认值,valueOf将被调用。 moment对象就是这种情况。

处理 + 运算符的过程如此复杂的原因是它还用于算术加法。

评估模板字面值时不存在这种复杂性,其中总是 字符串连接 ,因此将使用“字符串”提示调用 Symbol.toPrimitive 方法(而不是“默认”),或者如果该方法不存在,将调用 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>