从 ECMAscript 标准文档看懂 valueOf
最近在看 dayjs 的源码,源码上用到了 valueOf
方法,虽然知道这个方法,但是很少接触,就试着找来了ECMAscript标准文档来看看标准的定义。
首先看下标准对于 Object.prototype.valueOf
的定义:
关于 ToObject
,标准如下定义:
翻译过来就是:
参数类型 | 返回结果 |
---|---|
Undefined | 抛出 TypeError 异常 |
Null | 抛出 TypeError 异常 |
Boolean | 创建一个Boolean对象,初始值为参数值 |
Number | 创建一个Number对象,初始值为参数值 |
String | 创建一个String对象,初始值为参数值 |
Symbol | 创建一个Symbol对象,初始值为参数值 |
Object | 返回参数值 |
到了这里,貌似可以停止查阅文档了,但是我们不要忘了一个事情那就是原型链,这只是定义在Object对象原型链上的。我们还需要看看各种类型自身的原型上是否定义了 valueOf
方法:
果不其然,Boolean、Number、String、Symbol的原型上都有自己的 valueOf
方法,分别查阅规范:
Boolean
Number
String
Symbol
根据上面的规范,制成表格为:
在这里我们假设调用的都是该定义的方法的类型,如调用Boolean.prototype.valueOf()
方法的一个布尔类型的值。
方法 | 返回结果 |
---|---|
Boolean.prototype.valueOf() | 返回布尔值本身 |
Number.prototype.valueOf() | 返回数字本身 |
String.prototype.valueOf() | 返回字符串本身 |
Symbol.prototype.valueOf() | 返回Symbole本身 |
那么,或许有人问了那这几个重新定义的 valueOf
方法和定义在Object原型上的 valueOf
返回值有啥不同吗?当然不同呀,定义在Object对象上返回的是一个对象,而重新定义的方法返回的是一个值,如下
这就跟你用使用字面量定义数字和使用构造函数定义使用数字的区别一样!
既然我们都知道js完全可以通过使用 call
和 apply
来改变this指向,那么也就是说完全这些方法可以被任意类型的值使用,那么此时的返回值又是什么呢?
我们还是来看文档:
首先看 String.prototype.valueOf()
方法,规范中指出返回的值是抽象操作符 thisStringValue(value)
的返回值,而 thisStringValue(value)
的处理过程是这样子的:
其他几个的规范也类似:
所以归根到底这里需要解决的是这个形如 [[SymbolData]] internal slot
是啥子东东的问题(姑且全翻译为类型内部插槽吧)。
查询文档,可以看到规范对于 Internal slots
的说明:
大致意思是说 internal slots
不是对象的属性,不会被继承,初始值都是未定义的。那就可以初步认为 internal slots
是一个类似于属性但是不能被直接获取的值,同时StackOverflow上也有人出来解释这个问题 What is an “internal slot” of an object in JavaScript? 大致我们可以认为这是一个内部值。同时在文档中可以查阅到诸如 Set the value of O’s [[NumberData]] internal slot to n
之类的话语:
因此,大致可以认为拥有 [[NumberData]] internal slot
的为数字类型,拥有 [[StringData]] internal slot
为字符串类型。
按照这个思路,String.prototype.valueOf()
、Number.prototype.valueOf()
这些方法是不能被其他数据类型调用的,调用则会抛出 TypeError 异常。
测试:
完全符合!
接下来需要考虑的就是 Date、Math、functioin 等对象的原型是否定义了 valueOf
方法,通过搜索查阅手册可以发现在这些对象中只有Date对象重写了 valueOf
方法
继续查阅可以知道这个 time value
就是时间戳,从 getTime()
方法的描述也可以证实:
所以 Date对象的 valueOf
方法直接返回时间戳。
因此根据上述的查阅取证,总结一下 valueOf
:
定义在 Object
的原型上的 valueOf
方法内部调用了内部的方法 ToObject()
,而 ToObject()
方法除了 Undefined 和 null 之外都会返回一个对象。而 Boolean
、Number
、String
、Symbol
、Date
对象的原型则重写了 valueOf
方法,且传入不是该类型的参数参数时会报错,列表为:
Object.prototype.valueOf
参数类型 | 返回结果 |
---|---|
Undefined | 抛出 TypeError 异常 |
Null | 抛出 TypeError 异常 |
Boolean | 创建一个Boolean对象,初始值为参数值 |
Number | 创建一个Number对象,初始值为参数值 |
String | 创建一个String对象,初始值为参数值 |
Symbol | 创建一个Symbol对象,初始值为参数值 |
Object | 返回参数值 |
Boolean.prototype.valueOf
参数类型 | 返回结果 |
---|---|
Boolean | 返回值 |
其他 | 抛出 TypeError 异常 |
String.prototype.valueOf
参数类型 | 返回结果 |
---|---|
String | 返回值 |
其他 | 抛出 TypeError 异常 |
Number.prototype.valueOf
参数类型 | 返回结果 |
---|---|
Number | 返回值 |
其他 | 抛出 TypeError 异常 |
Symbol.prototype.valueOf
参数类型 | 返回结果 |
---|---|
Symbol | 返回值 |
其他 | 抛出 TypeError 异常 |
Date.prototype.valueOf
参数类型 | 返回结果 |
---|---|
Date | 返回时间戳 |
其他 | 抛出 TypeError 异常 |
参考:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: Rollup 介绍和使用
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论