{}+{} in Javascript
【译】by lynhao
涉及Javascript专有名词考虑不影响理解均不作翻译
最近,Gary Bernhardt在”Wat”的闪电讲座上指出Javascript的一个有趣的怪癖:当你添加对象或者数组的时候会得到意想不到的结果.这篇文章解释了这些结果
在javascript中添加常用的规则很简单:你只能添加数字和字符串,其他类型的值将会转换成他们其中的一种,为了理解它的转换的工作原理,我们首先需要先了解其他一些事情.每当提到段落(如§9.1)时,它就是指代ECMA-262语言标准(ECMAScript 5.1)。
让我们先从一个快速的复习开始.javascript有两种类型:primitives(基元) and objects(对象),原始类型包括:undefined,null, booleans,numbers,和strings,除此之外其他值都是对象,包括了数组和函数
1.转换值
加号运算符执行三种转换: 把值转换为primitives, numbers和strings
1.1 通过ToPrimitive()将值转化为primitives
ToPrimitive(input, PreferredType?)
可选参数PreferredType是Number 或 String。它只是表示一种
偏好, 结果总是可以是任何原始值。如果PreferredType是Number,则执行以下步骤来转换输入的值(§9.1):
- 如果输入是原始类型, 则直接返回它
- 否者, 输入的值是一个对象, 则调用obj.valueOf().如果结果返回是原始类型,则返回
- 否者,调用obj.toString. 如果结果是一个原始类型,则返回
- 否者,抛出一个TypeError异常
如果PreferredType 是String, 2和3步骤交换.如果缺失PreferredType, 那么对于Date的实例设置为String, 其他的值设置为Number
1.2 通过ToNumber()将值转换为数字
- undefined -> NaN
- null -> +0
- boolean -> ( true -> 1, false -> 0 )
- number -> 不需要转换
- string -> 解析字符串中的数字. 例如: “324”-> 324 (译者: 除字符串数字之外都转为 “NaN”)
1.3通过ToString()将值转换为字符串
- undefined -> “undefined”
- null -> + “null”
- oolean -> ( true -> “true” , false -> “false” )
- number -> 解析成字符串数字
- string -> 不需要转换
1.4 小试牛刀
下面object允许您观察转换过程
|
|
Number作为函数调用(跟构造函数相反)内部调用ToNumber():
|
|
2. 加法
给出下面的加法
|
|
为了分析上面的表达式, 采取以下步骤(§11.6.1):
- 将两个操作符转换为primitives (数学表示法,而不是JavaScript12prim1:= ToPrimitive(value1)rim2:= ToPrimitive(value2)
> PreferredType 被忽略因此Number为non-dates,String为dates
- 如果prim1或prim2是一个字符串,则将其转换为字符串并返回连接的结果
- 否者,将prim1和prim2都转换成数字并返回他们的总和
2.1 预期结果
添加两个数组,一切按预期运行
|
|
首先将[]转换为primitives尝试返回数组本身(this)的valueOf():
|
|
因为返回的结果不是原始类型,紧接着调用toString()返回空字符串(原始类型的值).因此[]+[]返回是两个空字符串联后的结果
添加数组和对象也符合我们的期望:
|
|
补充说明: 将空对象转换成字符串会得到下面的结果
|
|
之前的结果是””和 ‘[object Object]’ 串联后的返回的。
更多对象转换为primitives的示例:
|
|
2.2. 意外的结果
如果+的第一个操作符是一个空的对象字面量(如在FireFox的控制台看到的结果)情况就会变得很诡异
|
|
这里发生了什么?问题在于javascript将第一个{}解析成空的
代码块并忽略它.NaN是通过判断+{}(加号后跟第二个{})来计算的.
你在这里看到的加号不是二进制的加法运算符而是一个
一元前缀运算符,它用与Number()相同方式将其操作符转换
成数字.例如:
|
|
下面的表达式都是等效的:
|
|
为什么第一个{}被解析成代码块?因为完整的输入值被解析成语句,并且在语句的
花括号被解析成开始代码块.因此,你可以通过强制将输入值解析为表达式来解决问题:
|
|
function或者method的参数也总是被解析成表达式
|
|
前面解释之后,你应该不再对以下结果感到好奇了:
|
|
再分析一次, 被解析成代码块后跟着+[],以下表达式是等效的:
|
|
有趣的是,Node.js REPL分析其输入值与Firefox或Chrome不同(它甚至使用与Node.js相同的V8 JavaScript引擎.以下的输入值被解析成表达式结果并没有那么出乎意料
|
|
这样做的好处是更像使用输入值作为console.log()的参数时获得的结果。但它也不像使用输入值作为程序中的语句。
3.这是什么意思呢?
在大多数情况下,理解+在javascript运行机制并没有那么难.你只能添加数字或者字符串.
对象被转换为字符串(如果另一个操作符是字符串)或数字(否则),如果你想合并数组,你需要用一个方法:
|
|
Javascript中没有内置的方式去”拼接”(合并)对象.你需要使用库如Underscore:
|
|
注意:与Array.prototype.concat()不同,extend()修改它的第一个参数:
|
|
如果您对操作符有更多兴趣,可以阅读“Fake operator overloading in JavaScript”。
4.参考
- JavaScript values: not everything is an object
原文地址: http://2ality.com/2012/01/object-plus-object.html