许久没有写博客啦,最近有时间,将近来学到的一些有价值的东西整理下来
└(^_^)┘
废话不多说,开冲!
由类型判断引出 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]]
来实现的。看到这里其实我们并不陌生,因为在 es6
的 Symbol
章节也有类似的说明:

好的,现在我们知道了,实现 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
分析:
- 对于
a instanceof Number
,上文提到的 ECMA 规范中指出,instanceof
用来判断的是object
类型的,如果不是则会返回false
; - 对于
Number instanceof Number
以及String instanceof String
,都可以看做是Foo instanceof Foo
,因为Number
和String
都是构造函数,原型链上都是由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
以上