zoukankan      html  css  js  c++  java
  • JAVASCRIPT中{} + {}的结果是什么?

    转自: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

  • 相关阅读:
    css hack
    纯DIV+CSS制作的三级鼠标经过弹出下拉导航菜单源码
    题解 Luogu P3863 序列
    破解SA的密码的方法
    转 三种方法实现实时切换CSS样式
    SQL Server 性能优化工具(1)
    Sql server中时间查询的一个比较快的语句
    转 CodeForFun编写自动登录Email的程序
    ISAPI_rewrite中文手册
    ASP.NET中实现二级或多级域名(修改UrlRewrite)
  • 原文地址:https://www.cnblogs.com/jianxie/p/3623031.html
Copyright © 2011-2022 走看看