在你不知道的地方JS偷偷调用了valueOf和toString!

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)

控制台输出如下,你答对了吗?

控制台输出结果:

image-20220809115757055.png

image-20220809115923874

image-20220809115757055.png

全对的大佬可以出门左转了,出现错误的同学请继续往下看~

二、具体情况分析

针对直接使用cosole.log打印单独的对象,打印语句一在三种情况下都发挥正常。但是往下看,打印语句二console.log(fn)

image-20220809121806237

image-20220809121817987

image-20220809121828969

  • 方法返回值为1:打印函数体,自动调用toString()
  • 方法无返回值(undefined):打印函数体,自动调用toString()
  • 方法返回匿名函数:打印函数体,自动调用toString()之后调用valueOf()

从上面的结果中我们猜测,当打印的内容为函数时,会在内部隐式调用toString(),如果toString()返回基本数据类型,则数据转换到此为止。如果返回对象数据类型,则在toString()之后还会调用valueOf()。

打印语句3和4:

image-20220809121901299image-20220809121914502image-20220809121925724

  • 方法返回值为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()方法的返回值:

image-20220809110411560

toString方法返回值

20190719180630686

3.返回值:

在复写这两个函数时,函数返回值起着关键作用,可以决定调用几个方法和最后函数的返回值。好好利用这一特点与隐式自动调用的模式,助力成为装逼达人!

4.注意点:

alert会将两个方法最后得到的原始类型值进行返回

image-20220809113133100
但是console.log不会,还是返回函数体。

其他引用类型类似,可以自己动手试试。

四、问题

对于console.log这个操作不太理解,使用alert阻塞。可以看到先提示调用方法,但是确认完alert弹窗之后,居然console.log打印语句出现在了上面。实在是不理解,希望有懂的大佬不吝赐教。

image-20220809153623938 image-20220809153646505

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);

五、踩坑心得

事情是这样的,学习的时候看到一篇文章,对照着文章中的进行实践操作却得不到一样的结果,于是顺藤摸瓜摸出了这么多瓜。。。从柯里化到隐式调用到隐式类型转换。

image-20220809094045150

六、参考: