在 javascript 中添加两个空白对象或空白数组
Addition on two blank objects or blank arrays in javascript
当我发现以下控制台输出时,我正在玩 javascript:
[] + []
// output: ""
[] + {}
// output: [object Object]
{} + []
// output: 0
{} + {}
// output: NaN
任何人都可以向我解释这些输出背后的逻辑。我觉得这是一个非常奇怪的行为,但我想它可能有一些逻辑。
提前致谢。
预期结果
添加两个数组时,一切正常:
[] + []//output''
将 []
转换为基元首先尝试 valueOf()
其中 returns 数组本身 (this
):
var arr = [];
arr.valueOf() === arr
true
由于该结果不是原语,因此接下来调用 toString() 并且 returns 空字符串(这是原语)。因此,[] + []
的结果是两个空字符串的串联。
{} + [] // output: 0
添加数组和对象也符合我们的预期:
[] + {}//output '[object Object]'
解释:将空对象转换为字符串会产生以下结果。
String({})//output: '[object Object]'
前面的结果是通过连接 ""
和 "[object Object]"
创建的。
意外结果
如果 + 的第一个操作数是一个空对象字面量,事情就会变得很奇怪(在 Firefox 控制台上看到的结果):
{} + {}//output: NaN
这是怎么回事?问题是 JavaScript 将第一个 {}
解释为空代码块并忽略它。因此,NaN
是通过评估 +{}
(加上第二个 {}
)来计算的。这里看到的加号不是二元加法运算符,而是一元前缀运算符,将其操作数转换为数字,其方式与Number()
相同。例如:
+"3.65"
3.65
以下表达式都是等价的:
+{}
Number({})
Number({}.toString()) // {}.valueOf() isn’t primitive
Number("[object Object]")
NaN
为什么第一个 {}
被解释为代码块?因为完整的输入被解析为语句,语句开头的大括号被解释为代码块的开始。因此,您可以通过强制将输入解析为表达式来解决问题:
({} + {})//output: '[object Object][object Object]'
函数或方法的参数也总是被解析为表达式:
console.log({} + {})//output: [object Object][object Object]
经过前面的解释,你应该不会再对下面的结果感到惊讶了:
{} + []//output: 0
同样,这被解释为后跟 +[]
的代码块。以下表达式是等价的:
+[]
Number([])
Number([].toString()) // [].valueOf() isn’t primitive
Number("")
0
有趣的是,Node.js REPL 解析其输入的方式不同于 Firefox 或 Chrome(后者甚至使用与 Node.js 相同的 V8 JavaScript 引擎)。以下输入被解析为表达式,结果并不令人惊讶:
{} + {}//output: '[object Object][object Object]'
{} + []//output '[object Object]'
这样做的好处是更像将输入用作 console.log() 的参数时得到的结果。但它也不太像在程序中使用输入作为语句。
参考资料
当我发现以下控制台输出时,我正在玩 javascript:
[] + []
// output: ""[] + {}
// output: [object Object]{} + []
// output: 0{} + {}
// output: NaN
任何人都可以向我解释这些输出背后的逻辑。我觉得这是一个非常奇怪的行为,但我想它可能有一些逻辑。
提前致谢。
预期结果
添加两个数组时,一切正常:
[] + []//output''
将 []
转换为基元首先尝试 valueOf()
其中 returns 数组本身 (this
):
var arr = [];
arr.valueOf() === arr
true
由于该结果不是原语,因此接下来调用 toString() 并且 returns 空字符串(这是原语)。因此,[] + []
的结果是两个空字符串的串联。
{} + [] // output: 0
添加数组和对象也符合我们的预期:
[] + {}//output '[object Object]'
解释:将空对象转换为字符串会产生以下结果。
String({})//output: '[object Object]'
前面的结果是通过连接 ""
和 "[object Object]"
创建的。
意外结果
如果 + 的第一个操作数是一个空对象字面量,事情就会变得很奇怪(在 Firefox 控制台上看到的结果):
{} + {}//output: NaN
这是怎么回事?问题是 JavaScript 将第一个 {}
解释为空代码块并忽略它。因此,NaN
是通过评估 +{}
(加上第二个 {}
)来计算的。这里看到的加号不是二元加法运算符,而是一元前缀运算符,将其操作数转换为数字,其方式与Number()
相同。例如:
+"3.65"
3.65
以下表达式都是等价的:
+{}
Number({})
Number({}.toString()) // {}.valueOf() isn’t primitive
Number("[object Object]")
NaN
为什么第一个 {}
被解释为代码块?因为完整的输入被解析为语句,语句开头的大括号被解释为代码块的开始。因此,您可以通过强制将输入解析为表达式来解决问题:
({} + {})//output: '[object Object][object Object]'
函数或方法的参数也总是被解析为表达式:
console.log({} + {})//output: [object Object][object Object]
经过前面的解释,你应该不会再对下面的结果感到惊讶了:
{} + []//output: 0
同样,这被解释为后跟 +[]
的代码块。以下表达式是等价的:
+[]
Number([])
Number([].toString()) // [].valueOf() isn’t primitive
Number("")
0
有趣的是,Node.js REPL 解析其输入的方式不同于 Firefox 或 Chrome(后者甚至使用与 Node.js 相同的 V8 JavaScript 引擎)。以下输入被解析为表达式,结果并不令人惊讶:
{} + {}//output: '[object Object][object Object]'
{} + []//output '[object Object]'
这样做的好处是更像将输入用作 console.log() 的参数时得到的结果。但它也不太像在程序中使用输入作为语句。
参考资料