转自:http://www.heyria.com/index.php/2014/01/js-object-plus-object/

当对象或者数组相加的时候,会产生有点意外的结果。

这篇文章主要是解释为什么会产生这种结果。

在JavaScript中加号操作的规则比较简单:只能对Number或者String相加,其他的值都会被转化成这两种类型中的一种。为了理解这种转化是怎么工作的,我们首先弄清一些事情,参考ECMA-262v5版本 9.1章节

快速复习下,在JavaScript中有两种值:primitives 跟objects.原始类型的值有: undefined null BooleanNumber String.其他的所有值都是Object包括array跟function

1. 值的转换

加号运算符能执行三种转换:把值转化成primitive,数字跟字符串

1.1 通过ToPrimitive() 将值转换成原始类型

ToPrimitive(input, PreferredType?) 可选参数PreferredTypeNumber或者是String。返回值为任何原始值.如果PreferredTypeNumber,执行顺序如下:(参考:http://es5.github.io/#x9.1)

  1. 如果inputprimitive,返回
  2. 否则,inputObject。调用 obj.valueOf()。如果结果是primitive,返回。
  3. 否则,调用obj.toString(). 如果结果是primitive,返回
  4. 否则,抛出TypeError

如果 PreferredType是String,步骤2跟3互换,如果PreferredType没有,Date实例被设置成String,其他都是Number

1.2通过ToNumber()把值转换成Number

直接看ECMA 9.3的表格http://es5.github.io/#x9.3

  • undefined NaN
  • null +0
  • boolean value true is converted to 1, false is converted to +0
  • number value no conversion necessary
  • string value parse the number in the string. For example, “324″ is converted to 324 要注意的是Object的转换,首先调用ToPrimitive(obj, Number)方法,然后调用ToNumber作为结果。
1.3通过ToString()把值转化成字符串

直接看ECMA 9.8的表格http://es5.github.io/#x9.8

  • undefined “undefined”
  • null “null”
  • boolean value either “true” or “false”
  • number value the number as a string, e.g. “1.765″
  • string value no conversion necessary
1.4 试试看

下边的代码可以看到转换过程

var obj = {
valueOf: function () {
console.log("valueOf");
return {}; // not a primitive
},
toString: function () {
console.log("toString");
return {}; // not a primitive
}
}
Number(obj)//把obj转换成Number
//输出如下:
//valueOf 返回的不是原始类型,继续往下走
//toString 返回的不是原始类型,继续往下走
//TypeError: Cannot convert object to primitive value 抛出错误

2. 相加

value1 + value2 对此表达式求值时,遵循一下步骤(http://es5.github.io/#x11.6.1):

1.转换操作符两边的值为原始值

`prim1 := ToPrimitive(value1)`
`prim2 := ToPrimitive(value2)`

第二个参数PreferredType被忽略,所以对非Date类型都是Number,Date为String

2.如果prim1或者prim2有一个是String,把这俩值都转化成String,返回相连的结果。 3.否则,把prim1跟prim2都转化成Number返回相加后的结果

2.1 期望的结果

当你对两个数组相加的时候,结果跟预期的一样: [] + [] // '' 转化[]到primitive的时候,首先尝试valueOf(),返回数组本身(this):

var arr = [];
arr.valueOf() === arr
//true

因为结果不是primitive,继续调用toString(),返回空字符串(这个是primitive类型)。因此[] + []的结果是两个空字符串相连接,还是空字符串

数组跟对象相加,也能得到期望的结果:

[] + {}
//'[object Object]'

解释:将一个空对象转化为String,有以下结果

String({})
//'[object Object]'

所以是空字符串”"跟”[object Object]“的结合

更多例子:

5 + new Number(7)
12
6 + { valueOf: function () { return 2 } }
8
"abc" + { toString: function () { return "def" } }
'abcdef'
2.2 意料之外的结果

当空对象跟空对象相加的时候,事情变的有点怪了。。。

{} + {}
NaN

这里肿么了?问题在于JavaScript把第一个{},解析成空的代码块并且忽略它了。NaN实际上是后边这个+{}产生的。你在这里看到的加号,并不是二元元素符的那个加号,而是一元运算符,作用是,把值转换为Number,跟Number()方法一样,例如:

+"3.65"
3.65

下边的表达式是等价的:

+{}
Number({})
Number({}.toString()) // {}.valueOf() isn’t primitive
Number("[object Object]")
NaN

为什么第一个{}会被解析成代码块呢?因为这段代码被解析成statement并且{}在这个statement的起始,所以被当成block statement了。 然后,怎么修复呢–强制解析器把它认为是表达式:

({} + {})
'[object Object][object Object]'

function的参数,总是被当成表达式处理:

console.log({} + {})
[object Object][object Object]

经过这么一系列的解释,产生如下结果,你应该不会吃惊了:

{} + []
0

还是因为{}被解析成空代码块了,+[]返回0,下边这些表达式是等价的:

+[]
Number([])
Number([].toString()) // [].valueOf() isn’t primitive
Number("")
0

有趣的是,Node.js是用的REPL解释器,跟Firefox或者Chrome都不同(即使Node.js用的是跟Chroe一样的V8引擎)。Nodejs中会产生正常的结果:

 {} + {}
'[object Object][object Object]'
{} + []
'[object Object]'

3. 总结下

在绝大多数情况下,理解 +在JavaScript中是怎么工作的,并不难:只能对数字或者字符串相加。对象会被转换成字符串(如果一方是String)或者数字(木有String).如果你想连接数组,你需要使用一个方法:

[1, 2].concat([3, 4])
[ 1, 2, 3, 4 ]

在JavaScript中没有内置的方法可以连接对象。你需要使用一个类似Underscore一样的库:

var o1 = {eeny:1, meeny:2};
var o2 = {miny:3, moe: 4};
_.extend(o1, o2)
{ eeny: 1,
meeny: 2,
miny: 3,
moe: 4 }

注意:跟Array.prototype.concat()相反,extand()会修改它的第一个参数:

o1
{ eeny: 1,
meeny: 2,
miny: 3,
moe: 4 }
o2
{ miny: 3, moe: 4 }

翻译自http://www.2ality.com/2012/01/object-plus-object.html

JAVASCRIPT中{} + {}的结果是什么?的更多相关文章

  1. javascript中的Array对象 —— 数组的合并、转换、迭代、排序、堆栈

    Array 是javascript中经常用到的数据类型.javascript 的数组其他语言中数组的最大的区别是其每个数组项都可以保存任何类型的数据.本文主要讨论javascript中数组的声明.转换 ...

  2. javascript中的this与函数讲解

    前言 javascript中没有块级作用域(es6以前),javascript中作用域分为函数作用域和全局作用域.并且,大家可以认为全局作用域其实就是Window函数的函数作用域,我们编写的js代码, ...

  3. JavaScript 中的数据类型

    Javascript中的数据类型有以下几种情况: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Function,Date,Ar ...

  4. javascript中的操作符详解1

    好久没有写点什么了,根据博主的技术,仍然写一点javascript新手入门文章,接下来我们一起来探讨javascript的操作符. 一.前言 javascript中有许多操作符,但是许多初学者并不理解 ...

  5. 掌握javascript中的最基础数据结构-----数组

    这是一篇<数据结构与算法javascript描述>的读书笔记.主要梳理了关于数组的知识.部分内容及源码来自原作. 书中第一章介绍了如何配置javascript运行环境:javascript ...

  6. javascript中变量提升的理解

    网上找了两个经典的例子 var foo = 1; function bar() { if (!foo) { var foo = 10; } alert(foo); } bar(); // 10 var ...

  7. 前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型

    前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型 前言(题外话): 有人说拖延症是一个绝症,哎呀治不好了.先不说这是一个每个人都多多少少会有的,也不管它究竟对生活有多么大的 ...

  8. 简单分析JavaScript中的面向对象

    初学JavaScript的时候有人会认为JavaScript不是一门面向对象的语言,因为JS是没有类的概念的,但是这并不代表JavaScript没有对象的存在,而且JavaScript也提供了其它的方 ...

  9. Javascript中的valueOf与toString

    基本上,javascript中所有数据类型都拥有valueOf和toString这两个方法,null除外.它们俩解决javascript值运算与显示的问题,本文将详细介绍,有需要的朋友可以参考下. t ...

  10. 关于javascript中的this关键字

    this是非常强大的一个关键字,但是如果你不了解它,可能很难正确的使用它. 下面我解释一下如果在事件处理中使用this. 首先我们讨论一下下面这个函数中的this关联到什么. function doS ...

随机推荐

  1. ASP.NET Core 2.2 基础知识(三) 静态文件

    什么是静态文件? HTML,CSS,JS,图片等都叫做静态文件. 要想提供静态文件给客户端,需要注册静态文件中间件. 我们先分别添加一个 WebAPI 项目,一个 Razor 视图项目,比较两个项目的 ...

  2. Spring Struts里用到的设计模式

    Bean工厂的Factory模式 AOP的Proxy模式

  3. 【分块】bzoj2453 维护队列

    http://www.cnblogs.com/autsky-jadek/p/4020296.html 同bzoj2120. #include<cstdio> #include<cma ...

  4. URL Schemes(转载)

    URL Schemes 应用在 iOS 上已经很久了.对于使用者来说,在沙盒机制下的 iOS 中,如果想做到一定程度上的自动化就不可避免地要用到 URL Schemes.但因为 URL Schemes ...

  5. 选择改变事件OnCheckedChange

    1.效果图:选择正确的提示选对,选择错误提示选错 2.activity_main.xml <?xml version="1.0" encoding="utf-8&q ...

  6. 使用ReadOnlyCollection创建只读集合

    转载:http://www.cnblogs.com/abatei/archive/2008/02/04/1064102.html 使用泛型创建只读集合 问题 您希望类中的一个集合里的信息可以被外界访问 ...

  7. vb6 wininet

    Private Const UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; .NET CLR 1.1.4322)&qu ...

  8. 【FTP】java FTPClient 文件上传内容为空,文件大小为0

    问题:如题所述,使用FTPClient上传至FTP服务器, 表现如下:①文件大小为0 ②上传很小的文件,但是要花费很长的时间,20K要花费2分钟甚至更久 ③没有任何的报错,没有任何的乱码 解决方法: ...

  9. OpenGL ES2.0编程三步曲 -转

    原地址:http://blog.csdn.net/myarrow/article/details/7707943 1. 保存全局变量的数据结构 以下例子程序均基于Linux平台. typedef st ...

  10. Vue生命周期各阶段发生的事情

    首先,参考之前一篇vue生命周期的总结:Vue生命周期总结 接下来我们来分析下官方文档经典流程图,每个阶段到底发生了什么事情. 1.在beforeCreate和created钩子函数之间的生命周期 在 ...