-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
如何准确判断一个对象的类型? #11
Comments
多realm的环境 指的是什么啊? |
一般指的是一个页面上iframe之间以及iframe和parent之间 |
thx |
文末说的,没法区分装箱的类型,这是什么意思啊? |
仔细阅读,文章里已经解释了。 |
我就是不理解 装箱 这个词 |
装箱就是指把primitive值包装成对象。 参见 https://en.wikipedia.org/wiki/Object_type_(object-oriented_programming) |
thx ,受教 |
作为前端的同学,我们应该都知道可以使用typeof和instanceof在运行时判断JavaScript对象的类型。
对于原始类型(primitive type)的数据,大部分可使用typeof。在JavaScript中,primitive类型包括Null、Undefined、Boolean、Number、String、Symbol,如果算上Stage3的,还有BigInt。
这里说大部分,是因为除了一个例外,那就是Null。
上述7种原始类型的数据中,除了
typeof null
返回"object"
之外,其他的都返回对应类型名的小写字母。而
typeof null
返回"object"
是因为历史原因,这也不是我们讨论的重点,大家只要记住typeof null === 'object'
这个例外就好。除了原始类型外,对象返回
'object'
,函数返回'function'
。那么我们如果要判断不同类型的对象,就不能用typeof
了:那么我们判断对象类型的时候,可以使用instanceof:
💡 注意instanceof是能匹配类型的父类的,所以
arr instanceof Array
和arr instanceof Object
都是true,因为Object是Array的父类。满足
class extends
和原型链规则的父子类关系的对象都能被匹配:注意如果我们修改obj的原型链能改变
instanceof
的结果:实际上,只要一个类型Type的prototype在一个对象obj的原型链上,那么
obj instanceof Type
就是true,否则就是false。instanceof 的局限性
如果在realm的情况下,比如页面上包含iframe,将当前页面上的对象传给iframe执行,使用instanceof判断就会出问题,我们看一个简单的例子:
上面的例子里,在当前window中,
arr instanceof Array
是true,但是到了sandbox里面,parent.arr instanceof Array
变成false。这是因为,两个Array类型在不同的realm中,实际上要使用:parent.arr instanceof parent.Array
,这样返回的就是true。而typeof是字符串比较,自然不受此影响:
👉🏻【冷知识】结论:使用instanceof判断的时候,在多realm环境中要小心使用。
用 constructor 判断
有时候我们不希望匹配父类型,只希望匹配当前类型,那么我们可以用constructor来判断:
当然和instanceof的问题一样,遇到多realm的环境,constructor判断要确保类型是和判断的对象在同一个realm下。不过我们如果想匹配不同realm,在一些特殊情况下,我们可以使用constructor的只读属性name:
👉🏻对象的constructor会返回它的类型,而类型在定义的时候,会创建一个name只读属性,值为类型的名字。
不过使用constructor.name有非常大的限制,如果使用定义匿名的class,那么name就变成空的:
另外如果使用es-modules,我们import的类名不一定是包里面的类名。
再者,如果我们使用脚本压缩工具,那么文件中的类名会被替换为短名,那样的话,name属性的名字也随着改变了。
所以依赖constructor.name来判断不是一个好的方案。
Array.isArray
如果我们只是针对数组来判断,那么我们可以使用Array.isArray。
这个方法能够判断一个对象是否是一个Array类型或者其派生类型。
Array.isArray在多realm中能正常判断:
Array.isArray 给我们带来启发,既然在多realm环境中,使用instanceof不安全,那么我们可以构造类似Array.isArray的方法来实现我们自己的isType方法。
那么我们需要给予类型的实例一个标志,以使得我们能够根据这一标志来判断:
为了避免暴露isFooInstanceTag这样的属性名,这篇文章使用了Symbol.for,这样更好:
注意这里必须使用
Symbol.for
而不能直接使用Symbol
,因为在不同的realm下,同样key的Symbol.for
返回的是相同ID。stringTag
如果你看过一些库的早期实现,你会发现使用Object.prototype.toString来做类型判断的方式:
比如这是requirejs里面的代码片段。
在早期的JS中,不支持Array.isArray时,很多库是利用这个方法来判断数组的,同样我们还可以判断其他类型:
不过注意不要使用stringTag判断Number、Boolean等primitive类型,因为它没法区分装箱的类型:
像上面的代码,
1.0
和new Number(1.0)
的stringTag都返回[object Number]
,但是我们一般认为1.0和new Number(1.0)
是两个不同的类型。在ES2015之前,我们不能自定义类型的stringTag,我们自己定义的任何类型实例的stringTag都返回
[object Object]
。👉🏻 但是现在,我们可以通过实现
Symbol.toStringTag
的getter来自定义类型的stringTag:好了,以上是类型判断相关的几种办法,如果你还有什么想要讨论的,欢迎在issue中留言。
The text was updated successfully, but these errors were encountered: