深入理解instanceof实现原理

许久没有写博客啦,最近有时间,将近来学到的一些有价值的东西整理下来└(^_^)┘
废话不多说,开冲!


由类型判断引出 instanceof

在 JavaScript 中,有7种基本类型(包括6种原始类型以及对象类型)。
在某些场景下,可能会需要我们做类型的判断,通常我们使用 typeof 。但是 typeof 在进行类型判断时有局限—— typeof 对于对象来说,除了函数都会显示 object 。因此,我们可以考虑使用 instanceof 来进行类型判断,因为 instanceof 内部机制是通过原型链来实现的

	function foo() {
		console.log('我是函数')
	}
	let obj = {
		name: 'wyh',
		age: '23'
	}
	let arr = [1, 2, 3]

	console.log(typeof(123))      // number
	console.log(typeof('haha'))   // string
	console.log(typeof(true))     // boolean
	console.log(typeof(foo))      // function
	console.log(typeof(obj))      // object
	console.log(typeof(arr))      // object

instanceof 判断继承

刚才提到 instanceof 内部机制是通过原型链实现的,所以不难想到,可以通过 instanceof 来进行继承关系的判断。

	function Foo() {
		this.name = 'wyh'
		this.age = '23'
	}

	let foo = new Foo()
	console.log(foo instanceof Foo) // true

同样的,在多层继承关系中,instanceof 同样适用:

	function Foo() {
		this.name = 'wyh'
		this.age = '23'
	}

	function GFoo() {
		this.country = 'China'
	}

	Foo.prototype = new GFoo()

	let foo = new Foo()
	console.log(foo instanceof Foo)  // true
	console.log(foo instanceof GFoo) // true

instanceof 实现原理

上面看过 instanceof 其实是通过原型链来实现继承关系的判断。那么我们如何来手写一个函数来实现 instanceof 的功能呢。首先,我们通过 ECMA 标准可以得到关于 instanceof 的底层原理:

看着好像有点东西,但其实我们只需要关注 instanceof 实现实际上是调用 JS 内部函数 [[HasInstance]] 来实现的。看到这里其实我们并不陌生,因为在 es6Symbol 章节也有类似的说明:

好的,现在我们知道了,实现 instanceof 时,实际调用的是 HasInstance 这个内置方法。那么我们来看一下,ECMA 中是如何描述 HasInstance 的:

翻译成代码,即为:

function instance_of(L, R) {         // L 表示instanceof左边,R 表示instanceof右边
		let O = R.prototype;         // 取 R 的显示原型
		L = L.__proto__;             // 取 L 的隐式原型
		while (true) {               // 循环执行,直到 O 严格等于 L
			if (L === null) return false;
			if (O === L) return true;
			L = L.__proto__;
		}
	}

instance_of 函数即是 instanceof 操作符的代码实现,并需要注意传入的参数都要为 object 类型。


instanceof 易错问题

我们通过上文已经知道, instanceof 是通过原型链来实现继承关系判断以及类型判断的。那么 instanceof 有没有弊端呢?或者说它就是判断类型的终极方法了吗? 并不是!
我们来看几个问题:

	function Foo() {
		console.log('我是函数')
	}

	let a = 1
	console.log(a instanceof Number)       // false
	console.log(Number instanceof Number)  // false
	console.log(String instanceof String)  // false
	console.log(Foo instanceof Foo)        // false

分析:

  1. 对于 a instanceof Number ,上文提到的 ECMA 规范中指出,instanceof 用来判断的是 object 类型的,如果不是则会返回 false
  2. 对于 Number instanceof Number 以及 String instanceof String ,都可以看做是 Foo instanceof Foo ,因为 NumberString 都是构造函数,原型链上都是由 Function 实例化出来的。那么我们只需要分析 Foo instanceof Foo 即可将这三个搞懂,将 { L : Foo , R : Foo } 代入到由 ECMA 规范得出的 instance_of 函数中去,即可得到:
 		let O = R.prototype = Foo.prototype
	    L = L.__proto__ = Foo.__proto__ = Function.prototype

	    O !== L     // 循环
	    L = L.__proto__.__proto__ = Function.prototype.__proto__ = Object.prototype

		O !== L     // 循环
		L = L.__proto__.__proto__.__proto__ = Object.prototype.__proto__ = null

	    return false

以上


Programming is an art form.
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值