前言
今天在使用instanceof的时候发现有一段代码的结果我不是很理解,如下:
let num = 123;
console.log(num instanceof Number);// 打印的结果是false
对这个结果,我并不知道是为什么,经过翻阅ECMAScript文档得到了答案。
过程
刚开始查看了MDN文档的介绍是这样:
instanceof
运算符用于检测构造函数的prototype
属性是否出现在某个实例对象的原型链上。
注意,“某个实例对象”这个词很重要,我忽略了这一个词,导致我后面以为MDN的介绍错误,实际上这个介绍用词十分精准。
我用
Object.getPrototypeOf(num)
得出的结果是:
let num = 123;
console.log(Object.getPrototypeOf(num));
说明num是有____proto____的
接着我用以下代码测试num的____proto____是否和Array的prototype一致
let num = 123;
console.log(Object.getPrototypeOf(num) === Number.prototype);// true
打印结果为
true
,这就奇怪了,按照MDN文档的介绍,只要在原型链上就会返回true
啊,为什么实际上是返回的false
。到这里我感觉自己作为一个初学者能找到比较权威的文档的错误还是很兴奋,为了确认我的猜想,我找到了ECMAscript官方的文档
1998年提出的,instanceof规范提出的时候,我还没出生
文档的解释
因为我的英文并不好,所以靠着翻译软件和自己的猜想,得到了一个答案,如有错误,请在评论区指出。
instanceof的左边需要是一个‘关系表达式’,右边需要是一个‘标识符’
1.分析关系表达式
2.获取第1步结果的值
3.分析标识符
4.获取第3步结果的值
5.如果第2步的结果不是对象,返回false(这也就是为什么我之前的测试代码返回false的最根本原因)
6.如果第4步的结果不是一个函数对象,返回false
7.获取第4步结果(那个函数对象)的prototype参数
8.如果第7步的结果(函数对象的prototype值)不是一个对象,返回false(这里有一个疑惑,什么函数的prototype值能不是一个对象,在不人为修改的前提下)
9.让变量T等于第7步的结果(函数对象的prototype值)
10.让变量P等于第2步结果(需要进行instanceof判断的对象)的[[Prototype]](一般浏览器用____proto____显示)
11.如果P是null,返回false(也就是说null instanceof Function object
的值都是false)
12.如果P和T相等,返回true
13.让P = P的[[Prototype]](这就是为什么只要函数对象在实例对象的原型链上就可以返回true
的底层原因)
14.返回第10步
磕磕绊绊看完整个文档,其实讲得还是很清楚,我的疑惑也得到了解决,之后再仔细的看了看MDN文档,发现总结得竟然那么简单明了又正确。
自定义instanceof
既然都知道instanceof的底层原理了,自然手痒的我想要实现自己的一个instanceof
function myInstanceof(obj, fn) {
if (typeof obj !== 'object') return false;
if (typeof fn !== 'function') return false;
if (typeof fn.prototype !== 'object') return false;
let T = fn.prototype;
let P;
while (true) {
P = Object.getPrototypeOf(obj);
if (P === null) return false;
if (P === T) return true;
P = Object.getPrototypeOf(P);
}
}
总结
从底层规范知道了instanceof,不仅以后使用过程中会注意与typeof的区别,也通过阅读英文文档提高了自己阅读文档的能力。