在深度讲解js中的“+”号规则(第一篇)中,我们讲解了”+”号在基础对象中的“加法”行为,现在,我们继续讲解它在其他引用类型对象中的“加法”。
1、Object 的“加法”
1 2 3 4 5 6 7 8 9 10 11 12 |
var a = {} var b = { name: 1 } var c = { name: 1, toString: function(){ return 'object' } } console.log(a + b) // [object Object][object Object] console.log(a + c) // [object Object]object |
通过上面的代码,我想你已经知道了他们的原理,就是 Object 对象在进行 “+” 操作时,会先调用自己的 toString 方法,再进行字符串拼接。对于a 和 b 来说,我们没有给它们设置 toString 方法。它们就会调用自身的(原型上的) toString 方法,即 Object.prototype.toString。
2、Function 的“加法”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
var a = function(){} var b = function(){ return 'b' } var c = function(){ return 'c' } c.toString = function(){ return 'tostring' } console.log(a + b) /* 打印结果如下: function(){}function(){ return 'b' } */ console.log(a + c) /* 打印结果如下: function(){}tostring */ |
可以看出,Function 对象和 Object 对象一样,在进行 “+” 操作时,会先调用自己的 toString 方法,再进行字符串拼接,如果自定义了toString ,则调用自己的,否则调用原型上的。
但 Function 对象和 Object 对象又有区别,Function 的 toString 方法不是返回 [object Object],而是将整个整个方法体返回为字符串,连换行和缩进都完整保持。
对于 Function 的 toString,我们继续看如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
var fun1 = function funname(){ console.log('a'+"b") } var fun2 = (a,b) => a + b function fun3(){ cnsole.log(0) } console.log(fun1.toString()) console.log(fun2.toString()) console.log(fun3.toString()) /* 打印结果如下 function funname(){ console.log('a'+"b") } (a,b) => a + b function fun3(){ cnsole.log(0) } */ |
可以看到,对于 fun2 方法,toString 之后带上了函数名,所以我们可以这么认为,Function 的 toString,返回的是定义函数时,等号右边的全部内容字符串,因为对于fun3 来说,它其实等价于:
1 2 3 |
var fun3 = function fun3(){ cnsole.log(0) } |
3、Array 的“加法”
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var a = [] var b = [1,2] var c = [1,2] c.toString = function(){ return 'tostring' } console.log(a+1) // '1' console.log(b+1) // '1,21' console.log(a+b) // '1,2' console.log(a+c) // 'tostring' console.log(b+c) // '1,2tostring' console.log(b.toString()) // '1,2' |
可以看到,Array 的“加法”也遵循先 toString ,再拼接的逻辑。而且我们不难发现, Array 自己默认的 toString 方法,其原理等同于 Array.join(‘,’)。对于上面的 a + 1 来说, a toString 之后为 ”,而字符串跟数字相加,得到的结果是字符串,故而结果为 ‘1’ 而不是 1.
继续看下面代码:
1 2 |
console.log([1,2,['abc'],[4,5,{name:'abe'}]].toString()) // '1,2,abc,4,5,[object Object]' |
我们发现,不管是几维数组,toString方法都是一样的逻辑,因为对于上诉代码里面的二维数组 [‘abc’] 和 [4,5,{name:’abe’}]来说,它们也是数组,数组中的对象{name:’abe’},因为和前面的字符串拼接,也调用了自己的 toString 方法,得到 ‘[object Object]’,所以两个数组转为字符串就是 ‘abc’ 和 ‘4,5,[object Object]’,最后的结果也就是打印结果了。
通过上面3点,结合归纳总结思想,我们似乎得到了一个猜想,就是
非基础数据类型的对象,在遇到 “+” 操作时,都是先调用自己的 toString 方法,自己没有就找原型上的,得到结果之后再拼接。只是每种对象默认的 toString 方法实现不一样而已。
为了验证我们的猜想,我们再来试一下 Date 对象和 RegExp 对象,先看 toString 方法:
1 2 3 4 5 6 |
var date = new Date() var reg = new RegExp() console.log(date.toString()) // Thu Oct 31 2019 16:52:57 GMT+0800 (中国标准时间) console.log(reg.toString()) // /(?:)/ |
再来验证一下我们之前的猜想
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var date1 = new Date() var date2 = new Date() date2.toString = () => 'date1自己的toString' var reg1 = new RegExp() var reg2 = new RegExp() reg2.toString = () => 'reg2自己的toString' console.log(date1 + '__abc') console.log(date2 + '__abc') console.log(reg1 + '__abc') console.log(reg2 + '__abc') /** 运行结果如下 Thu Oct 31 2019 17:00:01 GMT+0800 (中国标准时间)__abc date1自己的toString__abc /(?:)/__abc reg2自己的toString__abc */ |
有没有发现,果不其然啊,跟我们的猜想一模一样。
话虽如此,还有些对象我们没有验证,所以从严谨的角度讲,我们不能以偏概全。对于那些还没验证的,我们就不下结论了。
所以,这里仅对上一篇的基础数据类型和本篇验证过的对象类型做一个结论。
在js中,’+’ 操作符既可用于数字的加法,也用于字符串拼接;
数字的加法仅限于操作符两边都是数字和一边是数字,另一边是可转化为数字的其他值(字符串除外,因为字符串和数字在一起就拼接了);
除此之外的其他情形,都是字符串拼接,基础数据类型有自己的转换为字符串的规则,而引用类型的变量,都是先调用自己的 toString 方法,自己没有就找原型上的,得到结果之后再拼接。只是每种对象默认的 toString 方法实现不一样而已。
发表评论