V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
rabbbit
V2EX  ›  JavaScript

[老生长谈]js 加法的规则总结

  •  
  •   rabbbit · 2018-08-18 16:19:25 +08:00 · 3295 次点击
    这是一个创建于 2318 天前的主题,其中的信息可能已经有所发展或是发生改变。

    自己总结了下 js 加法的密之转换规则,如果有错误还请多多指教

    其实基本的转换规则只有 3 条
    1 如果有对象,则先调用内部函数 ToPrimitive(先调用 valueOf,如果未返回基本数据类型再调用 toString)将其转换为基本数据类型(Undefined Null Boolean Number String)
    2 如果有一个是字符串,将另一个转换成字符串(调用内部函数 ToString),返回两个字符串相加的结果
    3 如果有一个是数值,将另一个转换成数值(调用内部函数 ToNumber),返回两个数字相加的结果

    注 规则 2 的优先级高于规则 3.同时有字符串和数值时,把数值转成字符串

    例子

    'a' 是字符串,因此将另一个元素转换成字符串(通过 ToString)

    'a' + 1 // a1
    'a' + true // "atrue"
    'a' + null // "anull"
    'a' + undefined // "aundefined"
    

    1 是数字,因此将另一个元素转为数字(通过 ToNumber)

    1 + true // 2
    1 + undefined // NaN
    1 + null // 1
    

    []是对象,则通过 ToPrimitive 转换为字符串后相加

    ({}).valueOf() // []
    // 返回来的是个对象,继续调用 toString
    [].toString() // ""
    
    [] + 1// "1"
    [] + 'a' // "a"
    

    {}是对象,则通过 ToPrimitive 转换为字符串后相加

    ({}).valueOf() // {}
    // 返回来的是个对象,继续调用 toString
    ({}).toString() // "[object Object]"
    
    1 + {} // "1[object Object]"
    'a' + {} // "a[object Object]"
    

    与对象相加一定返回字符串吗?不一定

    var n = new Number(1)
    typeof n // "object" n 是个对象,而非数值
    n.valueOf() // 1 返回的是 Number,就不再调用 toString 了
    n +  1 // 2 
    // 因此 new Number(1) + 1 返回的是数值而不是字符串
    

    如果修改 n 的 valueOf 方法,让其返回对象,则 n + 1 的结果是字符串而非数值

    n.valueOf = function(){return []}
    n + 1 // "11"
    

    {}的异常

    {} + 1 // 1
    {} + 'a' // NaN
    {} + '1' // 1
    {} + [] // 0
    

    在 firefox 和 ie 中(暂时不管 chrome),浏览器会把{}当作代码块.实际上执行的是一元加操作(在内部调用 ToNumbe),相当于

    +1 // 1
    +'a' // NaN
    +'1' // 1
    +[] // 0
    

    给{}外面加个括号,结果就又不一样了.浏览器会正常解析为加法

    ({}) + 1 // "[object Object]1"
    ({})+ 'a' //"[object Object]a"
    ({})+ '1' // "[object Object]1"
    ({})+ [] // "[object Object]"
    

    或者变成赋值语句也行

    var a = {} + 1
    console.log(a) // "[object Object]1"
    a = ({})+ 'a' //"[object Object]a"
    

    奇怪的 Chrome

    chrome 是最让人摸不着头脑的
    如果两个元素都是{}, 则将第一个元素当作对象处理(而非代码块)

    {} + {} // "[object Object][object Object]"
    {} + {a:1} // "[object Object][object Object]"
    

    如果第二个元素不是{}(数组 字符串等),则将第一个元素当作代码块(实际上执行的是一元加操作)

    {} + [] // 0
    {} + 1 // 1
    {} + 'a' // NaN
    {} + '1' // 1
    {} + null // 0
    

    不过还是可以通过套括号来解决

    ({}) + [] // "[object Object]"
    
    12 条回复    2019-05-04 09:10:45 +08:00
    orwell1995
        1
    orwell1995  
       2018-08-18 16:22:51 +08:00
    我想说,真的有必要纠结这些吗...
    rabbbit
        2
    rabbbit  
    OP
       2018-08-18 16:23:28 +08:00
    @orwell1995 面试爱考我也没办法
    pelloz
        3
    pelloz  
       2018-08-18 16:27:18 +08:00
    面试考这些垃圾知识的公司不去也罢
    isbase
        4
    isbase  
       2018-08-18 16:30:15 +08:00
    整天研究这种糟糠,没有任何实际意义,写出这种代码的人直接滚蛋
    azh7138m
        5
    azh7138m  
       2018-08-18 16:42:14 +08:00
    https://tc39.github.io/ecma262/#sec-addition-operator-plus-runtime-semantics-evaluation

    是说的这个顺序吧,我也好奇是哪家公司会问这个
    msputup
        6
    msputup  
       2018-08-18 17:54:26 +08:00 via Android
    说到面试,前几天去面试,问我
    var obj1={'arr':1}

    var obj2=obj1

    obj2.arr=2

    obj1.arr 是多少,我说当然是 2 啊,引用类型。

    然后又说,那我定义一个 function,传入 obj2,然后修改 arr=2 呢,我说还是 2 啊

    他说不对,这个传入的是值,obj1.arr 还是 1。

    后来搞得我都怀疑自己了,回家 console.log 一下,发现自己没说错啊,日了
    newghost
        7
    newghost  
       2018-08-18 19:34:47 +08:00
    @msputup

    你应该理解错了,

    var obj1={'arr':1}
    var obj2=obj1
    var add = function(arr) { arr = 2 }
    add(obj2.arr)
    console.log(obj1)
    > {'arr':1}
    8qwe24657913
        8
    8qwe24657913  
       2018-08-18 20:02:41 +08:00   ❤️ 1
    几个修正:
    1. 规则第一条,优先级 wtf[Symbol.toPrimitive]('default') > wtf.valueOf() > wtf.toString(),基本数据类型还包括 symbol 和 bignum (思考: new Date() + 1 与 new Date().valueOf() + 1,Date 对象的表现在 ES5 中是强行写在规范里的,ES6 添加了 Symbol.toPrimitive 使它变得 "正常" 了一点)
    2. 规则第三条,前提 "如果有一个是数值" 应改为 "否则" (思考: true + null)
    3. Chrome 的 Console 做了特殊处理,以 "{" 开头以 "}" 结尾会被尝试加上小括号 (思考: {a:1} 和 {} + function(){} 以及 {} * class{})
    msputup
        9
    msputup  
       2018-08-18 20:57:58 +08:00 via Android
    @newghost 那看来是你说的这题,但是我反复确认下,他跟我说的传入的是对象,尴尬了
    lihongjie0209
        10
    lihongjie0209  
       2018-08-18 21:15:45 +08:00
    js 这种东西, 生态不行, 面试题也不行
    skinny
        11
    skinny  
       2018-08-19 15:28:38 +08:00
    我非常好奇为什么要问这些奇葩问题,如果没有编程约定,为什么不检查类型,或者转成字符串再拼接,一定要一股脑把不相同的东西用加号处理吗?
    pinews
        12
    pinews  
       2019-05-04 09:10:45 +08:00
    @skinny
    @isbase
    @pelloz
    @orwell1995 道理是这个道理,但谁能避免没有人手贱写出这样的东西,
    我们常常调侃一年前写的代码,突然发现自己看不懂了,专家说阅读代码比写代码更难,所以我们一边要求写代码尽可能的易读,但另一方面,如果真的遇到了,你不能撒手不管吧。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2275 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 16:01 · PVG 00:01 · LAX 08:01 · JFK 11:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.