先从instanceof说起
在日常开发中,我们通常使用typeof去判断一个数据类型或者是否为undefined。
但是其结果只能判断出基本的数据类型,即为:number, string, object, boolean, function, undefined等。所以在进行例如数组和对象判断时,我们需要使用instanceof来检测某个数据是否为Array或Object的实例。
例如:1
2
3
4
5const a = new Array();
const b = new Object();
console.log(a instanceof Array);
console.log(a instanceof Object);
我们会发现 输出的结果都为true。这个时候可能会疑惑,这样一来无法判断出a到底是Array还是Object的实例了。
我们接下来继续:
1 | console.log(Array instanceof Object); |
从这里我们看出来Array本身也是继承于Object类。
那么我们怎么判断ab的数据类型呢?只需要多做一层判断
1 | console.log(b instanceof Array); |
前者返回false,后者返回true。这说明了b是一个对象而非数组。而a在第一次判断就可以确定是一个数组对象。a instanceof Object的判断是多余的。
instanceof 的实现
那么我们现在可以说instanceof 是用来判断生成实例对象所对应的构造函数么?那Array instanceof Object和extends继承有什么关系?
下面我们来实现一个instanceof:
1 | function instanceOf (A, B) { |
代码很简单,参数A就是instance左边的数据,首先取A的隐式原型(可以理解为对象中的原型,它本质上等于构造函数的原型,这也是我们用来做instanceof判断的基准)。
接下来,就是取B的原型。这里做了一个while循环,终止条件就是一直向上查找到Object.prototype的隐式原型,这里要注意,不管是实例对象还是函数原型,都包含了一个隐式原型对象。
在查询匹配到Object.prototype.proto的时候已经到达了终点就是null。这里可以理解为并没有匹配到对应的B的原型。
方式就是通过获取隐式原型向上查找,如匹配到A的隐式原型和B的原型相同则返回true。
这里我们再通过代码理解一下Array instanceof Object 为 true:
孔子说过:在js当中一切皆为对象。Array 可以理解为一个函数,也可以理解为是一个对象。
那么有如下代码:
1 | Array.__proto__ === Function.prototype; (true) |
九拐十八弯,我们终于发现其实 Array.proto.proto 往上2层寻找到了对应的Object原型。
这里值得注意的是,Function的隐式原型等于Function的原型。这个可能有点怪异,后面会另外做讲解。
准备上我呕心沥血制作的美图了
到这里,可能不太熟悉原型,原型链,构造函数以及相互之间的关系的同学已经要懵逼了。下面我通过原创的关系图来做一个形象的说明。
如果这张图还不能满足你对于原型的理解,我可以尽量说的再详细一点:
js饼干工厂
首先让我们脑洞一下,js世界里有一个饼干工厂。我就是厂长啦,既然是饼干工厂那肯定是要生产饼干的。
下面我要给饼干做一个市场定位,是专门生产8-14岁的青少年儿童饼干,所有的饼干都富含多种促发育的营养物质,这个就是Object.prototype。
他是一切饼干产品及制作的根源(一切皆为对象)
接下来建立产品饼干的研发中心Function.prototype, 它设立了好几条产线例如:
String, Number, Function, Object, Array, Boolean等函数,当然他们也有自己的prototype 独立的生产配方。但是他们都是基于这个研发中心来生产的。
所以上述函数的隐式原型就是来源于Function.prototype。
另外Function可以理解为处理产品的实验室而非产线,它既可以实例出新的函数,其本身又可以作为对象可以查询到自身的隐式原型指向。
最后通过这些函数可以new 出各种不同种类的饼干例如{}, [], 1, ‘23’, true等等。而这些实例的对象的隐式原型又指向生产他们的配方。
换句话说Object.prototype是一切对象的原型定义, Function.prototype是一切函数的原型定义。但是函数本身也是对象,所以就有了Function.prototype.__prototype =
Object.prototype. 这样一来Object.prototype站在了js原型的最顶端。
通过原型链的层次可以这样来排列:
Object.prototype
Function.prototype
Object, Function, Symbol, Boolean, String, Number
{}, function, symbol, true, ‘123’, 123
自定义的function本身还可以自定义原型并实例化新的对象{…}