在 JS 中判断一个变量的类型常常会用 typeof 运算符,但是对于引用类型存储值就会出现问题,所以引入了 instanceof 来解决问题。instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例的原型链上。
用法:
object instanceof constructor
举例:
let simpleStr = 'This is a simple string';
simpleStr instanceof String; // false, 检查原型链会找到 undefined
let myString = new String();
myString instanceof String; // true
myString instanceof Object; // true
需要注意的是,如果表达式返回 true,并不意味着该表达式会永远返回 true,因为 constructor.prototype 属性的值可能会改变改变之后的值很有可能不存在于 object 的原型链上面。另外一种情况就是 object 的原型链情况改变也会造成表达式结果的改变。
// 情况1: 改变构造函数的 prototype 属性值
function Cat() {}
function Dog() {}
const cat = new Cat()
console.log(cat instanceof Cat) // true
Cat.prototype = new Dog()
console.log(cat instanceof Cat) // false
// 情况2: 改变实例的原型链情况
const dog = new Dog()
console.log(dog instanceof Dog) // true
dog.__proto__ = new String()
console.log(dog instanceof Dog) // false
语言规范的定义
看几个例子:
console.log(Object instanceof Object); // true
console.log(Function instanceof Function); // true
console.log(Number instanceof Number); // false
console.log(String instanceof String); // false
function Foo(){}
console.log(Function instanceof Object); // true
console.log(Foo instanceof Function); // true
console.log(Foo instanceof Foo); // false
为什么 Object 和 Function instanceof 自己都返回 true,但是其他类却不等于 true 呢?接下来我们就从语言规范中是如何定义这个运算符的理解一下 instanceof。
11.8.6 The instanceof operator The production RelationalExpression: RelationalExpression instanceof ShiftExpression is evaluated as follows:
Evaluate RelationalExpression.
Call GetValue(Result(1)). // 调用 GetValue 方法得到 Result(1) 的值,设为 Result(2)
Evaluate ShiftExpression.
Call GetValue(Result(3)). // 同理,这里设为 Result(4)
If Result(4) is not an object, throw a TypeError exception. // 如果 Result(4) 不是 object,抛出异常
If Result(4) does not have a [[HasInstance]] method, throw a TypeError exception. // 如果 Result(4) 没有 [[HasInstance]] 方法,抛出异常。规范中的所有 [[...]] 方法或者属性都是内部的,在 JavaScript 中不能直接使用。并且规范中说明,只有 Function 对象实现了 [[HasInstance]] 方法。所以这里可以简单的理解为:如果 Result(4) 不是 Function 对象,抛出异常.
Call the [[HasInstance]] method of Result(4) with parameter Result(2). // 相当于这样调用:Result(4).[[HasInstance]](Result(2))
Return Result(7).
相关的 HasInstance 方法定义:
15.3.5.3 [[HasInstance]] (V) Assume F is a Function object. // 这里 F 就是上面的 Result(4),V 是 Result(2) When the [[HasInstance]] method of F is called with value V, the following steps are taken:
1. If V is not an object, return false. // 如果 V 不是 object,直接返回 false 2. Call the [[Get]] method of F with property name "prototype". // 用 [[Get]] 方法取 F 的 prototype 属性 3. Let O be Result(2).//O = F.\[\[Get]]("prototype") 4. If O is not an object, throw a TypeError exception. 5. Let V be the value of the [[Prototype]] property of V.//V = V.[[Prototype]] 6. If V is null, return false. 7. If O and V refer to the same object or if they refer to objects joined to each other (section 13.1.2), return true. // 这里是关键,如果 O 和 V 引用的是同一个对象,则返回 true;否则,到 Step 8 返回 Step 5 继续循环 8. Go to step 5.
上面的规范定义比较晦涩,看起来比较复杂,涉及到很多概念,但是把这段翻译成 JS 代码却很简单,如下:
function instance_of(L, R) { // L 表示左表达式,R 表示右表达式
const O = R.prototype;
L = L.__proto__;
while(true) {
if (L === null) { // Object.prototype 为 null
return false
}
if (O === L) { // 当 O 严格等于 L 时,返回 true
return true
}
L = L.__proto__
}
}
涉及到原型链的,可以参考之前写的文章,【JS基础】原型和原型链。
这里我们就可以跟据上面的代码来分析一下为什么 Object 和 Function instanceof 自己都返回 true,但是其他类却不等于 true。
首先 Object 和 String 这些本身都属于函数,他们的构造函数都是 Function,所以 Object instanceof Function 返回 true,Function.prototype 的构造函数是 Object,所以 Function.prototype instanceof Object 返回 true,所以 Object 和 String instanceof Object 和 Function 的时候都会返回 true。
Object instanceof Object; // true
Object.__proto__ === Function.prototype // true
Function.prototype.__proto__ === Object.prototype // true
String instanceof String; // false
String.__proto__ === Function.prototype // true
Function.prototype.__proto__ === Object.prototype // true
Object.prototype.__proto__ === null // 根据上面代码,如果 L 为 null 时,返回 false