【前端面试小册】JS-第32节:实现 instanceof
一、核心概念
1.1 instanceof 的作用
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
语法:object instanceof constructor
返回值:true 或 false
1.2 基本使用
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log([] instanceof Object); // true
console.log(2 instanceof Number); // false(基本类型)
console.log(new Number(2) instanceof Number); // true
二、实现原理
2.1 基础实现
function myInstanceof(left, right) {
let _left = left.__proto__;
let _right = right.prototype;
while (true) {
if (_left === null) {
return false;
}
if (_left === _right) {
return true;
}
_left = _left.__proto__;
}
}
2.2 原理解析
执行流程:
graph TD
A[调用 myInstanceof] --> B[获取 left 的 __proto__]
B --> C[获取 right 的 prototype]
C --> D{left.__proto__ === null?}
D -->|是| E[返回 false]
D -->|否| F{left.__proto__ === right.prototype?}
F -->|是| G[返回 true]
F -->|否| H[向上查找 __proto__]
H --> D
关键点:
- 沿着
left的原型链向上查找 - 比较
left.__proto__和right.prototype - 如果找到则返回
true,到达null则返回false
2.3 执行示例
// 示例:[] instanceof Array
// 步骤 1: left = [], right = Array
// 步骤 2: _left = [].__proto__ = Array.prototype
// 步骤 3: _right = Array.prototype
// 步骤 4: _left === _right,返回 true
// 示例:[] instanceof Object
// 步骤 1: left = [], right = Object
// 步骤 2: _left = [].__proto__ = Array.prototype
// 步骤 3: _right = Object.prototype
// 步骤 4: _left !== _right,继续向上
// 步骤 5: _left = Array.prototype.__proto__ = Object.prototype
// 步骤 6: _left === _right,返回 true
三、优化版本
3.1 添加参数验证
function myInstanceof(left, right) {
// 参数验证
if (left === null || left === undefined) {
return false;
}
if (typeof right !== 'function') {
throw new TypeError('Right-hand side of instanceof is not callable');
}
let _left = left.__proto__;
let _right = right.prototype;
while (true) {
if (_left === null) {
return false;
}
if (_left === _right) {
return true;
}
_left = _left.__proto__;
}
}
3.2 使用 Object.getPrototypeOf
function myInstanceof(left, right) {
if (left === null || left === undefined) {
return false;
}
if (typeof right !== 'function') {
throw new TypeError('Right-hand side of instanceof is not callable');
}
let proto = Object.getPrototypeOf(left);
const prototype = right.prototype;
while (proto !== null) {
if (proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
优点:
- 使用标准 API
Object.getPrototypeOf - 不依赖
__proto__(非标准属性)
3.3 完整版本
function myInstanceof(left, right) {
// 基本类型直接返回 false
if (left === null || left === undefined) {
return false;
}
// right 必须是函数
if (typeof right !== 'function') {
throw new TypeError('Right-hand side of instanceof is not callable');
}
// 基本类型的包装对象需要特殊处理
if (typeof left !== 'object' && typeof left !== 'function') {
return false;
}
// 沿着原型链向上查找
let proto = Object.getPrototypeOf(left);
const prototype = right.prototype;
while (proto !== null) {
if (proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
四、测试用例
4.1 基础测试
// 测试 1:数组
const res = myInstanceof([], Array);
console.log(res); // true
// 测试 2:对象
const res1 = myInstanceof({}, Object);
console.log(res1); // true
// 测试 3:数组是 Object 的实例
const res2 = myInstanceof([], Object);
console.log(res2); // true
// 测试 4:基本类型
const res3 = myInstanceof(2, Number);
console.log(res3); // false
4.2 复杂测试
// 测试 5:自定义类
class Person {}
class Student extends Person {}
const student = new Student();
console.log(myInstanceof(student, Student)); // true
console.log(myInstanceof(student, Person)); // true
console.log(myInstanceof(student, Object)); // true
// 测试 6:函数
function fn() {}
console.log(myInstanceof(fn, Function)); // true
console.log(myInstanceof(fn, Object)); // true
// 测试 7:Date
const date = new Date();
console.log(myInstanceof(date, Date)); // true
console.log(myInstanceof(date, Object)); // true
// 测试 8:RegExp
const regex = /test/;
console.log(myInstanceof(regex, RegExp)); // true
4.3 边界情况
// 测试 9:null 和 undefined
console.log(myInstanceof(null, Object)); // false
console.log(myInstanceof(undefined, Object)); // false
// 测试 10:基本类型
console.log(myInstanceof('string', String)); // false
console.log(myInstanceof(true, Boolean)); // false
// 测试 11:包装对象
console.log(myInstanceof(new String(''), String)); // true
console.log(myInstanceof(new Number(2), Number)); // true
五、与原生的区别
5.1 行为一致性
// 原生 instanceof
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
// 我们的实现
console.log(myInstanceof([], Array)); // true
console.log(myInstanceof({}, Object)); // true
5.2 错误处理
// 原生 instanceof
try {
[] instanceof null;
} catch (e) {
console.log(e.message); // Right-hand side of instanceof is not callable
}
// 我们的实现也应该抛出相同的错误
try {
myInstanceof([], null);
} catch (e) {
console.log(e.message); // Right-hand side of instanceof is not callable
}
六、实际应用场景
6.1 类型检查
function processData(data) {
if (myInstanceof(data, Array)) {
return data.map(item => processItem(item));
}
if (myInstanceof(data, Object)) {
return Object.keys(data).reduce((acc, key) => {
acc[key] = processData(data[key]);
return acc;
}, {});
}
return data;
}
6.2 参数验证
function createUser(userData) {
if (!myInstanceof(userData, Object)) {
throw new TypeError('userData must be an object');
}
if (!myInstanceof(userData.name, String)) {
throw new TypeError('userData.name must be a string');
}
// 创建用户
return new User(userData);
}
七、面试要点总结
核心知识点
- instanceof 原理:沿着原型链向上查找
- 实现方式:循环比较
__proto__和prototype - 边界情况:null、undefined、基本类型
- 标准 API:使用
Object.getPrototypeOf更规范
常见面试题
Q1: 如何实现 instanceof?
答:沿着对象的原型链向上查找,比较 __proto__ 和构造函数的 prototype,如果找到则返回 true,到达 null 则返回 false。
Q2: instanceof 和 typeof 的区别?
答:
typeof:返回基本类型字符串,无法区分对象类型instanceof:判断对象是否为某个构造函数的实例,可以判断对象类型
Q3: 为什么基本类型 instanceof 返回 false?
答:instanceof 检查的是对象,基本类型不是对象,所以返回 false。需要使用包装对象(如 new Number(2))才能返回 true。
实战建议
- ✅ 理解原型链的查找机制
- ✅ 掌握
__proto__和prototype的关系 - ✅ 注意边界情况的处理
- ✅ 使用标准 API
Object.getPrototypeOf
前端面试小册 文章被收录于专栏
每天更新3-4节,持续更新中... 目标:50天学完,上岸银行总行!
