1. instanceof 的基本原理
instanceof 运算符用于检测构造函数的 prototype 属性 是否出现在某个实例对象的原型链上。
js
obj instanceof Constructor
// 检查 Constructor.prototype 是否在 obj 的原型链上
2. JavaScript 内部实现原理
原型链查找过程:
- 获取对象的原型:
Object.getPrototypeOf(obj) - 获取构造函数的原型:
Constructor.prototype - 沿着对象的原型链向上查找,看是否能找到构造函数的原型
伪代码表示:
js
function instanceof(obj, Constructor) {
// 1. 基本类型直接返回 false
if (obj === null || typeof obj !== 'object' && typeof obj !== 'function') {
return false;
}
// 2. 获取对象的原型
let proto = Object.getPrototypeOf(obj);
// 3. 获取构造函数的原型
const prototype = Constructor.prototype;
// 4. 沿着原型链向上查找
while (proto !== null) {
if (proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
3. 自定义实现
完整的手写实现:
js
function myInstanceof(instance, Constructor) {
// 基本类型直接返回 false
if (instance === null || typeof instance !== 'object' && typeof instance !== 'function') {
return false;
}
// 构造函数必须是函数
if (typeof Constructor !== 'function') {
throw new TypeError('Right-hand side of instanceof is not callable');
}
// 获取构造函数的原型
const prototype = Constructor.prototype;
// 获取实例的原型
let proto = Object.getPrototypeOf(instance);
// 沿着原型链向上查找
while (proto !== null) {
if (proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
使用示例:
js
// 测试用例
function Person(name) {
this.name = name;
}
function Student(name, grade) {
Person.call(this, name);
this.grade = grade;
}
// 设置原型继承
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
const stu = new Student('Alice', 90);
console.log(myInstanceof(stu, Student)); // true
console.log(myInstanceof(stu, Person)); // true
console.log(myInstanceof(stu, Object)); // true
console.log(myInstanceof(stu, Array)); // false
// 边界情况测试
console.log(myInstanceof(null, Object)); // false
console.log(myInstanceof(undefined, Object)); // false
console.log(myInstanceof(123, Number)); // false (原始类型)
console.log(myInstanceof(new Number(123), Number)); // true (对象类型)
4. 特殊情况与注意事项
基本类型检测:
js
// 原始类型
console.log('str' instanceof String); // false
console.log(new String('str') instanceof String); // true
// 使用 Object() 包装
console.log(Object('str') instanceof String); // true
边界情况:
js
// 1. 构造函数不是函数
try {
[] instanceof {}; // TypeError: Right-hand side of 'instanceof' is not callable
} catch(e) {
console.log(e.message);
}
// 2. 修改构造函数的 prototype
function Foo() {}
const obj = new Foo();
// 修改前
console.log(obj instanceof Foo); // true
// 修改原型
Foo.prototype = {};
// 修改后
console.log(obj instanceof Foo); // false (因为 obj.__proto__ 指向的是旧的 Foo.prototype)
Symbol.hasInstance 自定义行为:
js
class MyClass {
static [Symbol.hasInstance](instance) {
// 自定义 instanceof 行为
return Array.isArray(instance);
}
}
console.log([] instanceof MyClass); // true
console.log({} instanceof MyClass); // false
5. 性能优化版本
对于高频使用场景,可以进一步优化:
js
function fastInstanceof(instance, Constructor) {
// 快速失败条件
if (instance == null ||
typeof Constructor !== 'function' ||
typeof instance !== 'object' && typeof instance !== 'function') {
return false;
}
// 缓存原型,减少属性访问
const prototype = Constructor.prototype;
// 使用 while 循环,比递归性能更好
let proto = instance.__proto__; // 或 Object.getPrototypeOf(instance)
while (proto) {
if (proto === prototype) return true;
proto = proto.__proto__;
}
return false;
}
6. 与 typeof 和 isPrototypeOf 的区别
js
// instanceof 检查原型链
console.log([] instanceof Array); // true
console.log([] instanceof Object); // true
// typeof 检查原始类型
console.log(typeof []); // "object"
console.log(typeof function() {}); // "function"
// isPrototypeOf 从原型角度检查
console.log(Array.prototype.isPrototypeOf([])); // true
console.log(Object.prototype.isPrototypeOf([])); // true
// 三者的关系
function checkType(value) {
if (typeof value === 'object' && value !== null) {
if (Array.isArray(value)) {
return 'Array';
} else if (value instanceof Date) {
return 'Date';
} else if (value instanceof RegExp) {
return 'RegExp';
}
return 'Object';
}
return typeof value;
}
7. 实际应用场景
类型安全检查:
js
function processData(data) {
if (!(data instanceof Array)) {
throw new TypeError('Expected an array');
}
return data.map(item => item * 2);
}
// 或者更友好的版本
function safeProcess(data) {
if (Array.isArray(data)) {
return data.map(item => item * 2);
}
return [];
}
多重继承检测:
js
class Animal {}
class Mammal extends Animal {}
class Dog extends Mammal {}
const dog = new Dog();
// 检测继承链
console.log(dog instanceof Dog); // true
console.log(dog instanceof Mammal); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true
工厂函数类型检测:
js
function createShape(type) {
if (type === 'circle') {
return new Circle();
} else if (type === 'square') {
return new Square();
}
return new Shape();
}
function render(shape) {
if (shape instanceof Circle) {
drawCircle(shape);
} else if (shape instanceof Square) {
drawSquare(shape);
}
}
8. 注意事项
instanceof与跨窗口/iframe 问题:
js
// 不同 iframe 中的 Array 构造函数不相等
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
const iframeArray = iframe.contentWindow.Array;
const arr = new iframeArray(1, 2, 3);
console.log(arr instanceof Array); // false
console.log(Array.isArray(arr)); // true (更安全)
- 使用
Symbol.hasInstance改变行为:
js
class PrimitiveNumber {
static [Symbol.hasInstance](x) {
return typeof x === 'number';
}
}
console.log(123 instanceof PrimitiveNumber); // true
- 优先使用内置方法:
js
// 更好的数组检测
console.log(Array.isArray([])); // true (推荐)
console.log([] instanceof Array); // true
// 更好的原始类型检测
console.log(typeof 123 === 'number'); // 推荐
console.log(123 instanceof Number); // 不推荐
总结
instanceof 的核心原理是原型链查找。它的实现涉及:
- 获取对象的原型链
- 查找构造函数的
prototype属性是否在原型链上 - 沿着原型链向上递归查找
在实际开发中:
- 使用
instanceof检测自定义对象类型 - 使用
Array.isArray()检测数组 - 使用
typeof检测原始类型 - 注意跨窗口/iframe 的环境问题
- 可以使用
Symbol.hasInstance自定义检测逻辑