instanceof、原型链小结

78 阅读3分钟

instanceof

当我们判断一个数据为基本数据类型,比如StringNumberBoolean等,我们可以用typeof直接实现,但是如果是引用数据类型,这个typeof显得有点力不从心了,因为你看下面得例子

    let reg = /\d{1,4}/
    let obj = {}
    let arr = []
    console.log(typeof reg);  // object
    console.log(typeof obj);  // object
    console.log(typeof arr); // object

是不是你就没有办法用typeof实现了呢,javascript的语法规范在设计的时候已经考虑到这一点,给我们提供了一个instanceof完美解决了对象类型的判断。

instanceof 及其工作原理

首先,我们来看一下在mdn上是如何解释instanceof这个语法的解释: instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

语法

// object:某个instance、constructor:某个构造函数
object instanceof constructor

描述

instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上

var simpleStr = "This is a simple string";
var myString  = new String();
var newStr    = new String("String created with constructor");
var myDate    = new Date();
var myObj     = {};
var myNonObj  = Object.create(null);

simpleStr instanceof String; // 返回 false,非对象实例,因此返回 false
myString  instanceof String; // 返回 true
newStr    instanceof String; // 返回 true
myString  instanceof Object; // 返回 true

myObj instanceof Object;    // 返回 true,尽管原型没有定义
({})  instanceof Object;    // 返回 true,同上
myNonObj instanceof Object; // 返回 false,一种创建非 Object 实例的对象的方法

myString instanceof Date; //返回 false

myDate instanceof Date;     // 返回 true
myDate instanceof Object;   // 返回 true
myDate instanceof String;   // 返回 false

替代方案

Object.prototype.toString,我们可以利用这个方法来对一个变量的类型来进行比较准确的判断

Object.prototype.toString.call(1) // "[object Number]"

Object.prototype.toString.call('hi') // "[object String]"

Object.prototype.toString.call({a:'hi'}) // "[object Object]"

Object.prototype.toString.call([1,'a']) // "[object Array]"

Object.prototype.toString.call(true) // "[object Boolean]"

Object.prototype.toString.call(() => {}) // "[object Function]"

Object.prototype.toString.call(null) // "[object Null]"

Object.prototype.toString.call(undefined) // "[object Undefined]"

Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"

实现原理

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);

console.log(auto instanceof Car);
// expected output: true

console.log(auto instanceof Object);
// expected output: true

当然,instanceof 也可以判断一个实例是否是其父类型或者祖先类型的实例

let person = function () {
}
let programmer = function () {
}
programmer.prototype = new person()
let nicole = new programmer()
nicole instanceof person // true
nicole instanceof programmer // true

这是 instanceof 的用法,但是 instanceof 的原理是什么呢?根据 ECMAScript 语言规范,我梳理了一下大概的思路,然后整理了一段代码如下

function new_instance_of(leftVaule, rightVaule) { 
    let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
    leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
    while (true) {
    	if (leftVaule === null) {
            return false;	
        }
        if (leftVaule === rightProto) {
            return true;	
        } 
        leftVaule = leftVaule.__proto__ 
    }
}

其实 instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。

原型链小结

  • Function是javascript中的根对象,它不通过new操作符所创建,而是自己创建了自己
  • 构造函数才有原型对象constructor.prototype
  • __prop__是所有实例对象都有原型属性
  • 所有的构造函数Function这个顶级构造函数的实例

有了以上几句总结,几乎就涵盖了原型链所有的关系,万变不离其宗。

以上文章,参考了浅谈 instanceof 和 typeof 的实现原理