JavaScript中valueOf、toString的隐式调用
在你不知道的地方JS偷偷调用了valueOf和toString!
一、问题引入
你知道执行以下三种情况,控制台会打印什么吗?
(1)情况一:方法的返回值为1
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
| Array.prototype.toString = function () { console.log('array-----调用toString方法') return 1 } Array.prototype.valueOf = function () { console.log('array-----调用valueOf方法') return 1 } Function.prototype.toString = function () { console.log('function-----调用toString方法') return 1 } Function.prototype.valueOf = function () { console.log('function-----调用valueOf方法') return 1 }
let arr = new Array() let fn = function () { console.log('函数内打印') } console.log(arr) console.log(fn) console.log('console.log-----' + arr) console.log('console.log-----' + fn)
|
(2)情况二:方法无返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Array.prototype.toString = function () { console.log('array-----调用toString方法') } Array.prototype.valueOf = function () { console.log('array-----调用valueOf方法') } Function.prototype.toString = function () { console.log('function-----调用toString方法') } Function.prototype.valueOf = function () { console.log('function-----调用valueOf方法') }
let arr = new Array() let fn = function () { console.log('函数内打印') } console.log(arr) console.log(fn) console.log('console.log-----' + arr) console.log('console.log-----' + fn)
|
(3)情况三:方法返回值为一个匿名函数
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
| Array.prototype.toString = function () { console.log('array-----调用toString方法') return function () {} } Array.prototype.valueOf = function () { console.log('array-----调用valueOf方法') return function () {} } Function.prototype.toString = function () { console.log('function-----调用toString方法') return function () {} } Function.prototype.valueOf = function () { console.log('function-----调用valueOf方法') return function () {} }
let arr = new Array() let fn = function () { console.log('函数内打印') } console.log(arr) console.log(fn) console.log('console.log-----' + arr) console.log('console.log-----' + fn)
|
控制台输出如下,你答对了吗?
控制台输出结果:
全对的大佬可以出门左转了,出现错误的同学请继续往下看~
二、具体情况分析
针对直接使用cosole.log打印单独的对象,打印语句一在三种情况下都发挥正常。但是往下看,打印语句二console.log(fn)
- 方法返回值为1:打印函数体,自动调用toString()
- 方法无返回值(undefined):打印函数体,自动调用toString()
- 方法返回匿名函数:打印函数体,自动调用toString()之后调用valueOf()
从上面的结果中我们猜测,当打印的内容为函数时,会在内部隐式调用toString(),如果toString()返回基本数据类型,则数据转换到此为止。如果返回对象数据类型,则在toString()之后还会调用valueOf()。
打印语句3和4:
- 方法返回值为1:自动调用valueOf(),打印valueOf()返回值
- 方法无返回值(undefined):自动调用valueOf(),打印valueOf()返回值
- 方法返回匿名函数:自动调用valueOf()之后调用toString(),报错
有报错最好~从报的错误开始入手
TypeError: Cannot convert object to primitive value
翻译:不能将对象转换为原始值
查阅资料发现引用类型向原始类型转换遵循ToPrimitive规则
⭐️ToPrimitive规则
是引用类型向原始类型转变的规则,它遵循先valueOf后toString的模式期望得到一个原始类型。如果还是没法得到一个原始类型,就会抛出 TypeError。
三、总结
1.明确转换类型:
如alert,console.log明确引用类型是要转换为字符串类型,那么采用的策略是先toString后valueOf。
2.不明确转换类型:
如隐式类型转换,那么采用ToPrimitive规则,遵循先valueOf后toString的模式期望得到一个原始类型。
MDN中valueOf()方法的返回值:
toString方法返回值
3.返回值:
在复写这两个函数时,函数返回值起着关键作用,可以决定调用几个方法和最后函数的返回值。好好利用这一特点与隐式自动调用的模式,助力成为装逼达人!
4.注意点:
alert会将两个方法最后得到的原始类型值进行返回
但是console.log不会,还是返回函数体。
其他引用类型类似,可以自己动手试试。
四、问题
对于console.log这个操作不太理解,使用alert阻塞。可以看到先提示调用方法,但是确认完alert弹窗之后,居然console.log打印语句出现在了上面。实在是不理解,希望有懂的大佬不吝赐教。
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 26
| Array.prototype.toString = function () { console.log('array-----调用toString方法') alert('array-----调用toString方法') return 1 } Array.prototype.valueOf = function () { console.log('array-----调用valueOf方法') alert('array-----调用valueOf方法') return 1 } Function.prototype.toString = function () { console.log('function-----调用toString方法') alert('function-----调用toString方法') return 1 } Function.prototype.valueOf = function () { console.log('function-----调用valueOf方法') alert('function-----调用valueOf方法') return 1 } let arr = new Array() let fn = function () { console.log('函数内打印') } console.log(arr); console.log(fn);
|
五、踩坑心得
事情是这样的,学习的时候看到一篇文章,对照着文章中的进行实践操作却得不到一样的结果,于是顺藤摸瓜摸出了这么多瓜。。。从柯里化到隐式调用到隐式类型转换。
六、参考: