自己总结了下 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 是最让人摸不着头脑的
如果两个元素都是{}, 则将第一个元素当作对象处理(而非代码块)
{} + {} // "[object Object][object Object]"
{} + {a:1} // "[object Object][object Object]"
如果第二个元素不是{}(数组 字符串等),则将第一个元素当作代码块(实际上执行的是一元加操作)
{} + [] // 0
{} + 1 // 1
{} + 'a' // NaN
{} + '1' // 1
{} + null // 0
不过还是可以通过套括号来解决
({}) + [] // "[object Object]"
1
orwell1995 2018-08-18 16:22:51 +08:00
我想说,真的有必要纠结这些吗...
|
2
rabbbit OP @orwell1995 面试爱考我也没办法
|
3
pelloz 2018-08-18 16:27:18 +08:00
面试考这些垃圾知识的公司不去也罢
|
4
isbase 2018-08-18 16:30:15 +08:00
整天研究这种糟糠,没有任何实际意义,写出这种代码的人直接滚蛋
|
5
azh7138m 2018-08-18 16:42:14 +08:00
https://tc39.github.io/ecma262/#sec-addition-operator-plus-runtime-semantics-evaluation
是说的这个顺序吧,我也好奇是哪家公司会问这个 |
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 一下,发现自己没说错啊,日了 |
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} |
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{}) |
10
lihongjie0209 2018-08-18 21:15:45 +08:00
js 这种东西, 生态不行, 面试题也不行
|
11
skinny 2018-08-19 15:28:38 +08:00
我非常好奇为什么要问这些奇葩问题,如果没有编程约定,为什么不检查类型,或者转成字符串再拼接,一定要一股脑把不相同的东西用加号处理吗?
|
12
pinews 2019-05-04 09:10:45 +08:00
@skinny
@isbase @pelloz @orwell1995 道理是这个道理,但谁能避免没有人手贱写出这样的东西, 我们常常调侃一年前写的代码,突然发现自己看不懂了,专家说阅读代码比写代码更难,所以我们一边要求写代码尽可能的易读,但另一方面,如果真的遇到了,你不能撒手不管吧。 |