引言
在JavaScript中,new操作符和instanceof操作符是面向对象编程的核心概念。理解它们的底层原理不仅能帮助我们更好地使用JavaScript,还能在面试中展现出更扎实的基础知识。
你是否曾好奇:
- 当我们使用
new创建对象时,背后到底发生了什么? instanceof是如何检测对象类型的?- 原型链在对象创建和类型检测中扮演了什么角色?
通过手写实现这两个操作符, 我们将深入理解JavaScript的对象创建机制和原型链检测原理。
一、new操作符的深入理解
1.1 new操作符的作用
new操作符用于创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。
javascript
// 基本用法
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function () {
console.log(`Hello, I'm ${this.name}, ${this.age} years old.`);
};
const person = new Person("Alice", 25);
console.log(person.name); // Alice
person.sayHello(); // Hello, I'm Alice, 25 years old.
1.2 new操作符的执行过程
当我们执行new Constructor(...args)时, JavaScript引擎会执行以下步骤:
- 创建一个新对象: 创建一个空的简单JavaScript对象(即
{}) - 设置原型链: 将新对象的
[[Prototype]](即__proto__)指向构造函数的prototype属性 - 绑定this值: 将构造函数中的
this绑定到新创建的对象 - 执行构造函数: 执行构造函数中的代码(为新对象添加属性)
- 返回结果:
- 如果构造函数返回一个对象, 则返回这个对象
- 否则返回新创建的对象
二、手写new操作符
2.1 基础实现
javascript
/**
* 手写 new 操作符
* @param {Function} Constructor 构造函数
* @param {...any} args 构造函数参数
* @returns {Object} 新创建的对象
*/
function myNew(Constructor, ...args) {
// 1. 创建一个新对象,继承构造函数的原型
const obj = Object.create(Constructor.prototype);
// 2. 执行构造函数,绑定 this 到新对象
const result = Constructor.apply(obj, args);
// 3. 如果构造函数返回一个对象, 则返回该对象; 否则返回新对象
return result instanceof Object ? result : obj;
}
// 测试基础功能
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.introduce = function () {
console.log(`Hi, I'm ${this.name}, ${this.age} years old.`);
};
const person1 = new Person("Alice", 25);
const person2 = myNew(Person, "Bob", 30);
console.log(person1 instanceof Person); // true
console.log(person2 instanceof Person); // true
console.log(person2.name); // Bob
console.log(person2.age); // 30
person2.introduce(); // Hi, I'm Bob, 30 years old.
2.2 完整实现(处理边界情况)
javascript
/**
* 完整版 myNew 实现,处理各种边界情况
* @param {Function} Constructor 构造函数
* @param {...any} args 构造函数参数
* @returns {Object} 新创建的对象
*/
function myNewComplete(Constructor, ...args) {
// 参数验证
if (typeof Constructor !== "function") {
throw new TypeError("Constructor must be a function.");
}
// 1. 创建一个新对象, 继承构造函数的原型
// 使用 Object.create 而不是 {}, 确保原型链正确
const obj = Object.create(Constructor.prototype);
// 2. 执行构造函数, 绑定 this 到新对象
const result = Constructor.apply(obj, args);
// 3. 处理返回值
// 3.1 如果构造函数返回一个对象,则返回该对象
// 3.2 否则返回新创建的对象
const isObject = result !== null && typeof result === "object";
const isFunction = typeof result === "function";
return isObject || isFunction ? result : obj;
}
// 测试各种边界情况
console.log("=== 测试 myNewComplete ===");
// 测试1: 构造函数返回对象
function PersonWithReturnObject(name) {
this.name = name;
return { custom: "custom object" };
}
const test1 = myNewComplete(PersonWithReturnObject, "Alice");
console.log("测试1 - 构造函数返回对象:", test1); // { custom: 'custom object' }
console.log(
"测试1 - 不是 Person 实例:",
!(test1 instanceof PersonWithReturnObject)
); // true
// 测试2: 构造函数返回 null
function PersonWithReturnNull(name) {
this.name = name;
return null;
}
const test2 = myNewComplete(PersonWithReturnNull, "Bob");
console.log(
"测试2 - 构造函数返回 null:",
test2 instanceof PersonWithReturnNull
); // true
console.log("测试2 - name 属性:", test2.name); // Bob
// 测试3: 构造函数返回函数
function PersonWithReturnFunction(name) {
this.name = name;
return function () {
return "I am a function";
};
}
const test3 = myNewComplete(PersonWithReturnFunction, "Charlie");
console.log("测试3 - 构造函数返回函数:", typeof test3); // function
console.log("测试3 - 函数调用:", test3()); // I am a function
// 测试4: 构造函数返回基本类型
function PersonWithReturnPrimitive(name) {
this.name = name;
return 42;
}
const test4 = myNewComplete(PersonWithReturnPrimitive, "David");
console.log(
"测试4 - 构造函数返回基本类型:",
test4 instanceof PersonWithReturnPrimitive
); // true
console.log("测试4 - name 属性:", test4.name); // David
// 测试5: 构造函数没有返回值
function PersonNoReturn(name) {
this.name = name;
}
const test5 = myNewComplete(PersonNoReturn, "Eve");
console.log("测试5 - 构造函数没有返回值:", test5 instanceof PersonNoReturn); // true
console.log("测试5 - name 属性:", test5.name); // Eve
2.3 处理ES6类的版本
javascript
/**
* 完整版 myNew 实现,处理各种边界情况(包括 ES6 类)
* @param {Function} Constructor 构造函数
* @param {...any} args 构造函数参数
* @returns {Object} 新创建的对象
*/
function myNewComplete(Constructor, ...args) {
// 参数验证
if (typeof Constructor !== "function") {
throw new TypeError("Constructor must be a function.");
}
// 是哟哦那个 Reflect.construct 处理 ES6 类和普通函数
const instance = Reflect.construct(Constructor, args, Constructor);
// 对于普通函数, 处理返回值逻辑
// 判断是否是 ES6类(简单判断方式)
const isClass =
typeof Constructor === "function" &&
(Constructor.toString().startsWith("class") ||
Function.prototype.toString.call(Constructor).startsWith("class"));
if (!isClass) {
// 检查构造函数是否显式返回了对象/函数
const isObject = instance !== null && typeof instance === "object";
const isFunction = typeof instance === "function";
// 如果构造函数返回的不是继承自其原型的对象, 说明显式返回了其他值
if (!(instance instanceof Constructor)) {
return isObject || isFunction
? instance
: Object.create(Constructor.prototype);
}
}
return instance;
}
2.4 测试用例
javascript
console.log("=== 完整测试用例 ===");
// 测试类继承
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
console.log(`${this.name} (${this.breed}) barks.`);
}
}
// 测试 ES6 类
const dog1 = new Dog("Rex", "Labrador");
const dog2 = myNewComplete(Dog, "Buddy", "Golden Retriever");
console.log("dog1 instanceof Dog:", dog1 instanceof Dog); // true
console.log("dog2 instanceof Dog:", dog2 instanceof Dog); // true
console.log("dog1 instanceof Animal:", dog1 instanceof Animal); // true
console.log("dog2 instanceof Animal:", dog2 instanceof Animal); // true
dog1.speak(); // Rex (Labrador) barks.
dog2.speak(); // Buddy (Golden Retriever) barks.
// 测试内置构造函数
const date1 = new Date("2025-12-08");
const date2 = myNewComplete(Date, "2025-12-08");
console.log("date1 instanceof Date:", date1 instanceof Date); // true
console.log("date2 instanceof Date:", date2 instanceof Date); // true
console.log("date1.toISOString():", date1.toISOString()); // 2025-12-08T00:00:00.000Z
console.log("date2.toISOString():", date2.toISOString()); // 2025-12-08T00:00:00.000Z
// 测试错误情况
try {
myNewComplete(null);
} catch (error) {
console.log("错误测试1 - null:", error.message); // Constructor must be a function
}
try {
myNewComplete(42);
} catch (error) {
console.log("错误测试2 - 数字:", error.message); // Constructor must be a function
}
try {
myNewComplete("string");
} catch (error) {
console.log("错误测试3 - 字符串:", error.message); // Constructor must be a function
}
三、instanceof的深入理解
3.1 instanceof操作符的作用
instanceof操作符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。
javascript
function Animal() {}
function Dog() {}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
const dog = new Dog();
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true
console.log(dog instanceof Array); // false
3.2 instanceof的工作原理
instanceof操作符的算法可以描述为:
- 获取右侧操作数的
prototype属性 - 获取左侧操作数的
[[Prototype]]链(即__proto__链) - 沿着原型链向上查找, 如果找到与右侧
prototype相同的对象, 返回true - 如果到达原型链末端(
null)仍未找到, 返回false
四、手写 instanceof 操作符
4.1 基础实现
javascript
/**
* 手写 instanceof 操作符
* @param {any} instance 需要检测的对象
* @param {Function} Constructor 构造函数
* @returns {boolean} 检测结果
*/
function myInstanceof(instance, Constructor) {
// 基本类型直接返回false
if (
instance === null ||
(typeof instance !== "object" && typeof instance !== "function")
) {
return false;
}
// 获取构造函数的 prototype
const prototype = Constructor.prototype;
// 获取实例的原型
let proto = Object.getPrototypeOf(instance);
// 沿着原型链向上查找
while (proto !== null) {
if (proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
// 测试基础功能
function Animal() {}
function Dog() {}
function Cat() {}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
const dog = new Dog();
const cat = new Cat();
console.log("=== 测试 myInstanceof ===");
console.log("dog instanceof Dog:", myInstanceof(dog, Dog)); // true
console.log("dog instanceof Animal:", myInstanceof(dog, Animal)); // true
console.log("dog instanceof Object:", myInstanceof(dog, Object)); // true
console.log("dog instanceof Cat:", myInstanceof(dog, Cat)); // false
console.log("cat instanceof Dog:", myInstanceof(cat, Dog)); // false
4.2 完整实现(处理边界情况)
javascript
/**
* 完整版 myInstanceof 实现
* @param {any} instance 要检测的对象
* @param {Function} Constructor 构造函数
* @returns {boolean} 检测结果
*/
function myInstanceofComplete(instance, Constructor) {
// 参数验证
if (typeof Constructor !== 'function') {
throw new TypeError('Right-hand side of instanceof is not callable');
}
// 基本类型直接返回 false
// 注意:null 和 undefined 返回 false
if (instance === null || instance === undefined) {
return false;
}
// 对于非对象类型,除了 function 外都返回 false
const type = typeof instance;
if (type !== 'object' && type !== 'function') {
return false;
}
// 获取构造函数的 prototype
const prototype = Constructor.prototype;
// 如果构造函数的 prototype 不是对象,抛出错误
if (prototype === null || typeof prototype !== 'object') {
throw new TypeError('Function has non-object prototype in instanceof check');
}
// 获取实例的原型
let proto = Object.getPrototypeOf(instance);
// 沿着原型链向上查找
while (proto !== null) {
if (proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
// 测试完整功能
console.log('=== 测试 myInstanceofComplete ===');
// 测试1: 基本类型
console.log('测试1 - 数字:', myInstanceofComplete(42, Number)); // false
console.log('测试1 - 字符串:', myInstanceofComplete('hello', String)); // false
console.log('测试1 - 布尔值:', myInstanceofComplete(true, Boolean)); // false
console.log('测试1 - null:', myInstanceofComplete(null, Object)); // false
console.log('测试1 - undefined:', myInstanceofComplete(undefined, Object)); // false
// 测试2: 对象类型
function Person() {}
const person = new Person();
console.log('测试2 - 对象:', myInstanceofComplete(person, Person)); // true
console.log('测试2 - 对象 instanceof Object:', myInstanceofComplete(person, Object)); // true
// 测试3: 数组
const arr = [1, 2, 3];
console.log('测试3 - 数组:', myInstanceofComplete(arr, Array)); // true
console.log('测试3 - 数组 instanceof Object:', myInstanceofComplete(arr, Object)); // true
// 测试4: 函数
function testFunc() {}
console.log('测试4 - 函数:', myInstanceofComplete(testFunc, Function)); // true
console.log('测试4 - 函数 instanceof Object:', myInstanceofComplete(testFunc, Object)); // true
// 测试5: 内置对象
const date = new Date();
console.log('测试5 - Date:', myInstanceofComplete(date, Date)); // true
console.log('测试5 - Date instanceof Object:', myInstanceofComplete(date, Object)); // true
// 测试6: 原型链继承
function Animal() {}
function Dog() {}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
const dog = new Dog();
console.log('测试6 - 原型链:', myInstanceofComplete(dog, Dog)); // true
console.log('测试6 - 原型链:', myInstanceofComplete(dog, Animal)); // true
console.log('测试6 - 原型链:', myInstanceofComplete(dog, Object)); // true
// 测试7: 错误情况
try {
myInstanceofComplete({}, null);
} catch (error) {
console.log('测试7 - 非函数:', error.message); // 'Right-hand side of instanceof is not callable'
}
try {
myInstanceofComplete({}, {prototype: null});
} catch (error) {
console.log('测试7 - 非函数2:', error.message); // 'Right-hand side of instanceof is not callable'
}
4.3 测试用例
javascript
console.log('=== 完整测试用例 ===');
// 复杂原型链测试
function GrandParent() {}
function Parent() {}
function Child() {}
Parent.prototype = Object.create(GrandParent.prototype);
Parent.prototype.constructor = Parent;
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const child = new Child();
console.log('原型链测试 - child instanceof Child:', myInstanceofComplete(child, Child)); // true
console.log('原型链测试 - child instanceof Parent:', myInstanceofComplete(child, Parent)); // true
console.log('原型链测试 - child instanceof GrandParent:', myInstanceofComplete(child, GrandParent)); // true
console.log('原型链测试 - child instanceof Object:', myInstanceofComplete(child, Object)); // true
// Symbol.hasInstance 测试
class CustomClass {
static [Symbol.hasInstance](instance) {
return typeof instance === 'object' && instance.isCustom === true;
}
}
const customObj = { isCustom: true };
const notCustomObj = { isCustom: false };
console.log('Symbol.hasInstance 测试 - 自定义对象:', myInstanceofComplete(customObj, CustomClass)); // true
console.log('Symbol.hasInstance 测试 - 非自定义对象:', myInstanceofComplete(notCustomObj, CustomClass)); // false
// 边界情况:Object.create(null)
const objWithoutProto = Object.create(null);
console.log('Object.create(null) 测试:', myInstanceofComplete(objWithoutProto, Object)); // false
// 边界情况:修改原型链
const obj = {};
Object.setPrototypeOf(obj, Array.prototype);
console.log('修改原型链测试:', myInstanceofComplete(obj, Array)); // true
console.log('修改原型链测试:', myInstanceofComplete(obj, Object)); // true
// 边界情况:循环引用(应避免这种情况)
function A() {}
function B() {}
// 设置循环原型链(不推荐,仅用于测试)
const a = new A();
const b = new B();
// 注意:实际中不要这样做,这里只是为了测试
Object.setPrototypeOf(A.prototype, B.prototype);
Object.setPrototypeOf(B.prototype, A.prototype);
try {
// 这里可能会陷入无限循环,所以我们需要有保护机制
console.log('循环引用测试:', myInstanceofComplete(a, B));
} catch (error) {
console.log('循环引用测试 - 捕获错误:', error.message);
}
五、new和instanceof的综合应用
5.1 实现一个继承系统
javascript
// 使用 new 和 instanceof 实现完整的继承系统
function inherit(Child, Parent) {
// 1. 创建中间构造函数,避免直接修改 Child.prototype
const Temp = function () {};
Temp.prototype = Parent.prototype;
// 2. 设置 Child 的原型为 Parent 的实例
Child.prototype = new Temp();
// 3. 修复 constructor 指向
Child.prototype.constructor = Child;
// 4. 设置 superClass 引用,便于调用父类方法
Child.superClass = Parent.prototype;
// 5. 如果父类的原型是 Object,需要额外处理
if (Parent.prototype.constructor === Object) {
Parent.prototype.constructor = Parent;
}
}
// 测试继承系统
function Shape(color) {
this.color = color;
}
Shape.prototype.getColor = function () {
return this.color;
};
Shape.prototype.getArea = function () {
throw new Error("getArea() must be implemented by subclasses");
};
function Circle(color, radius) {
// 调用父类构造函数
Shape.call(this, color);
this.radius = radius;
}
// 继承 Shape
inherit(Circle, Shape);
// 添加子类方法
Circle.prototype.getArea = function () {
return Math.PI * this.radius * this.radius;
};
Circle.prototype.getCircumference = function () {
return 2 * Math.PI * this.radius;
};
// 测试
const circle = new Circle("red", 5);
console.log("继承测试 - color:", circle.getColor()); // red
console.log("继承测试 - area:", circle.getArea().toFixed(2)); // 78.54
console.log("继承测试 - circumference:", circle.getCircumference().toFixed(2)); // 31.42
console.log("继承测试 - instanceof Circle:", circle instanceof Circle); // true
console.log("继承测试 - instanceof Shape:", circle instanceof Shape); // true
console.log("继承测试 - instanceof Object:", circle instanceof Object); // true
5.2 实现多态检测
javascript
// 使用 instanceof 实现多态类型检测
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
class Cat extends Animal {
speak() {
console.log(`${this.name} meows.`);
}
}
class Bird extends Animal {
speak() {
console.log(`${this.name} chirps.`);
}
}
// 多态处理器
class AnimalHandler {
static handle(animal) {
if (animal instanceof Dog) {
console.log(`Handling a dog named ${animal.name}`);
animal.speak();
console.log('Taking the dog for a walk...');
} else if (animal instanceof Cat) {
console.log(`Handling a cat named ${animal.name}`);
animal.speak();
console.log('Giving the cat some milk...');
} else if (animal instanceof Bird) {
console.log(`Handling a bird named ${animal.name}`);
animal.speak();
console.log('Letting the bird fly...');
} else if (animal instanceof Animal) {
console.log(`Handling an unknown animal named ${animal.name}`);
animal.speak();
console.log('Providing general care...');
} else {
console.log('Not an animal!');
}
}
}
// 测试多态
const animals = [
new Dog('Rex'),
new Cat('Whiskers'),
new Bird('Tweety'),
new Animal('Generic'),
{ name: 'Not an animal' }
];
console.log('=== 多态处理测试 ===');
animals.forEach(animal => {
AnimalHandler.handle(animal);
console.log('---');
});
六、常见面试题
6.1 实现Object.create()
javascript
/**
* 手写 Object.create()
* @param {Object} proto 新创建对象的原型对象
* @param {Object} propertiesObject 可选,包含属性描述符的对象
* @returns {Object} 新对象
*/
function myObjectCreate(proto, propertiesObject) {
// 参数验证
if (proto !== null && typeof proto !== 'object' && typeof proto !== 'function') {
throw new TypeError('Object prototype may only be an Object or null');
}
// 创建一个空构造函数
function Temp() {}
// 设置构造函数的原型
Temp.prototype = proto;
// 创建新对象
const obj = new Temp();
// 如果 proto 是 null,手动设置原型为 null
if (proto === null) {
Object.setPrototypeOf(obj, null);
}
// 处理第二个参数
if (propertiesObject !== undefined) {
Object.defineProperties(obj, propertiesObject);
}
return obj;
}
// 测试 myObjectCreate
console.log('=== 测试 myObjectCreate ===');
// 测试1: 基本功能
const proto = { x: 10, y: 20 };
const obj1 = myObjectCreate(proto);
console.log('测试1 - 原型链:', Object.getPrototypeOf(obj1) === proto); // true
console.log('测试1 - 继承属性:', obj1.x, obj1.y); // 10, 20
// 测试2: null 原型
const obj2 = myObjectCreate(null);
console.log('测试2 - null 原型:', Object.getPrototypeOf(obj2)); // null
console.log('测试2 - 没有 toString:', obj2.toString === undefined); // true
// 测试3: 属性描述符
const obj3 = myObjectCreate({}, {
value: {
value: 42,
writable: false,
enumerable: true,
configurable: true
}
});
console.log('测试3 - 属性描述符:', obj3.value); // 42
obj3.value = 100;
console.log('测试3 - 不可写:', obj3.value); // 42 (不可写)
// 测试4: 与原生 Object.create 比较
const nativeObj = Object.create(proto);
const myObj = myObjectCreate(proto);
console.log('测试4 - 原生 vs 手写:',
Object.getPrototypeOf(nativeObj) === Object.getPrototypeOf(myObj)); // true
6.2 实现Object.getPrototypeOf()
javascript
/**
* 手写 Object.getPrototypeOf()
* @param {Object} obj 要获取原型的对象
* @returns {Object | null} 对象的原型
*/
function myGetPrototypeOf(obj) {
// 参数验证
if (obj === null || obj === undefined) {
throw new TypeError("Cannot convert undefined or null to object");
}
// 使用 __proto__ 访问器(非标准,但广泛支持)
// 注意: 实际中更推荐使用 Object.getPrototypeOf
if ("__proto__" in obj) {
return obj.__proto__;
}
// 对于不支持 __proto__ 的环境,使用 Constructor 属性
// 注意: 这种方法不可靠,因为 Constructor 可能被修改
if (obj.constructor && obj.constructor.prototype) {
return obj.constructor.prototype;
}
// 对于通过 Object.create(null) 创建的对象
return null;
}
// 测试 myGetPrototypeOf
console.log("=== 测试 myGetPrototypeOf ===");
// 测试1: 普通对象
const obj1 = {};
console.log("测试1 - 普通对象:", myGetPrototypeOf(obj1) === Object.prototype); // true
// 测试2: 数组
const arr = [];
console.log("测试2 - 数组:", myGetPrototypeOf(arr) === Array.prototype); // true
// 测试3: 自定义对象
function Person() {}
const person = new Person();
console.log(
"测试3 - 自定义对象:",
myGetPrototypeOf(person) === Person.prototype
); // true
// 测试4: null 原型
const nullProtoObj = Object.create(null);
console.log("测试4 - null 原型:", myGetPrototypeOf(nullProtoObj)); // null
// 测试5: 错误情况
try {
myGetPrototypeOf(null);
} catch (error) {
console.log("测试5 - null 错误:", error.message); // Cannot convert undefined or null to object
}
6.3 实现完整的原型链检测工具
javascript
/**
* 原型链检测工具类
*/
class PrototypeChainUtils {
/**
* 获取对象的完整原型链
* @param {Object} obj 要检测的对象
* @returns {Array} 原型链数组
*/
static getPrototypeChain(obj) {
if (obj === null || obj === undefined) {
throw new TypeError("Cannot convert undefined or null to object");
}
const chain = [];
let proto = Object.getPrototypeOf(obj);
while (proto !== null) {
chain.push(proto);
proto = Object.getPrototypeOf(proto);
}
return chain;
}
/**
* 检查对象是否在原型链上
* @param {Object} obj 要检查的对象
* @param {Object} target 目标原型
* @returns {boolean} 是否在原型链上
*/
static isInPrototypeChain(obj, target) {
if (obj === null || obj === undefined) {
return false;
}
let proto = Object.getPrototypeOf(obj);
while (proto !== null) {
if (proto === target) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
/**
* 获取对象到指定原型的距离
* @param {Object} obj 对象
* @param {Object} target 目标原型
* @returns {number} 距离(-1 表示不再原型链上)
*/
static getDistanceToPrototype(obj, target) {
if (obj === null || obj === undefined) {
return -1;
}
let distance = 0;
let proto = Object.getPrototypeOf(obj);
while (proto !== null) {
if (proto === target) {
return distance;
}
distance++;
proto = Object.getPrototypeOf(proto);
}
return -1;
}
}
// 测试原型链工具
console.log("=== 测试 PrototypeChainUtils ===");
function A() {}
function B() {}
function C() {}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
C.prototype = Object.create(B.prototype);
C.prototype.constructor = C;
const c = new C();
// 获取原型链
const chain = PrototypeChainUtils.getPrototypeChain(c);
console.log("原型链:", chain);
console.log("原型链长度:", chain.length); // 4
console.log("包含 C.prototype:", chain[0] === C.prototype); // true
console.log("包含 B.prototype:", chain[1] === B.prototype); // true
console.log("包含 A.prototype:", chain[2] === A.prototype); // true
console.log("包含 Object.prototype:", chain[3] === Object.prototype); // true
// 检查是否在原型链上
console.log(
"是否在原型链上 - C.prototype:",
PrototypeChainUtils.isInPrototypeChain(c, C.prototype)
); // true
console.log(
"是否在原型链上 - B.prototype:",
PrototypeChainUtils.isInPrototypeChain(c, B.prototype)
); // true
console.log(
"是否在原型链上 - A.prototype:",
PrototypeChainUtils.isInPrototypeChain(c, A.prototype)
); // true
console.log(
"是否在原型链上 - Object.prototype:",
PrototypeChainUtils.isInPrototypeChain(c, Object.prototype)
); // true
console.log(
"是否在原型链上 - 不存在的原型:",
PrototypeChainUtils.isInPrototypeChain(c, Array.prototype)
); // false
// 获取距离
console.log(
"到 C.prototype 的距离:",
PrototypeChainUtils.getDistanceToPrototype(c, C.prototype)
); // 0
console.log(
"到 B.prototype 的距离:",
PrototypeChainUtils.getDistanceToPrototype(c, B.prototype)
); // 1
console.log(
"到 A.prototype 的距离:",
PrototypeChainUtils.getDistanceToPrototype(c, A.prototype)
); // 2
console.log(
"到 Object.prototype 的距离:",
PrototypeChainUtils.getDistanceToPrototype(c, Object.prototype)
); // 3
console.log(
"到不存在的原型的距离:",
PrototypeChainUtils.getDistanceToPrototype(c, Array.prototype)
); // -1
七、实际应用场景
7.1 实现工厂模式
javascript
// 使用 new 和 instanceof 实现工厂模式
class VehicleFactory {
static createVehicle(type, options) {
switch (type) {
case 'car':
return new Car(options);
case 'truck':
return new Truck(options);
case 'motorcycle':
return new Motorcycle(options);
default:
throw new Error(`Unknown vehicle type: ${type}`);
}
}
static validateVehicle(vehicle) {
if (!(vehicle instanceof Vehicle)) {
throw new TypeError('Invalid vehicle');
}
return vehicle;
}
}
class Vehicle {
constructor(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
getInfo() {
return `${this.year} ${this.make} ${this.model}`;
}
}
class Car extends Vehicle {
constructor(options) {
super(options.make, options.model, options.year);
this.doors = options.doors || 4;
}
getInfo() {
return `${super.getInfo()} (${this.doors} doors)`;
}
}
class Truck extends Vehicle {
constructor(options) {
super(options.make, options.model, options.year);
this.payload = options.payload || '1 ton';
}
getInfo() {
return `${super.getInfo()} (${this.payload} payload)`;
}
}
class Motorcycle extends Vehicle {
constructor(options) {
super(options.make, options.model, options.year);
this.type = options.type || 'street';
}
getInfo() {
return `${super.getInfo()} (${this.type} motorcycle)`;
}
}
// 使用工厂模式
console.log('=== 工厂模式测试 ===');
try {
const car = VehicleFactory.createVehicle('car', {
make: 'Toyota',
model: 'Camry',
year: 2022,
doors: 4
});
const truck = VehicleFactory.createVehicle('truck', {
make: 'Ford',
model: 'F-150',
year: 2021,
payload: '2 tons'
});
const motorcycle = VehicleFactory.createVehicle('motorcycle', {
make: 'Harley-Davidson',
model: 'Sportster',
year: 2020,
type: 'cruiser'
});
console.log('Car:', car.getInfo());
console.log('Truck:', truck.getInfo());
console.log('Motorcycle:', motorcycle.getInfo());
// 验证
console.log('Car instanceof Vehicle:', VehicleFactory.validateVehicle(car) instanceof Vehicle);
console.log('Car instanceof Car:', car instanceof Car);
console.log('Truck instanceof Vehicle:', truck instanceof Vehicle);
console.log('Motorcycle instanceof Vehicle:', motorcycle instanceof Vehicle);
// 错误测试
try {
VehicleFactory.createVehicle('airplane', {});
} catch (error) {
console.log('错误处理:', error.message);
}
try {
VehicleFactory.validateVehicle({});
} catch (error) {
console.log('验证错误:', error.message);
}
} catch (error) {
console.error('工厂模式错误:', error);
}
7.2 实现插件系统
javascript
// 使用原型链实现插件系统
class PluginSystem {
constructor() {
this.plugins = new Map();
this.hooks = new Map();
}
// 注册插件
register(plugin) {
if (!(plugin instanceof Plugin)) {
throw new TypeError('Plugin must be an instance of Plugin');
}
const name = plugin.name;
if (this.plugins.has(name)) {
throw new Error(`Plugin "${name}" is already registered`);
}
this.plugins.set(name, plugin);
// 注册钩子
if (plugin.hooks) {
for (const [hookName, handler] of Object.entries(plugin.hooks)) {
if (!this.hooks.has(hookName)) {
this.hooks.set(hookName, []);
}
this.hooks.get(hookName).push(handler);
}
}
console.log(`Plugin "${name}" registered successfully`);
}
// 触发钩子
trigger(hookName, ...args) {
if (!this.hooks.has(hookName)) {
return [];
}
const results = [];
const handlers = this.hooks.get(hookName);
for (const handler of handlers) {
try {
const result = handler(...args);
results.push(result);
} catch (error) {
console.error(`Error in hook "${hookName}":`, error);
}
}
return results;
}
// 获取插件
getPlugin(name) {
return this.plugins.get(name);
}
// 检查插件是否已注册
hasPlugin(name) {
return this.plugins.has(name);
}
}
// 插件基类
class Plugin {
constructor(name) {
if (new.target === Plugin) {
throw new TypeError('Cannot instantiate Plugin directly');
}
this.name = name;
this.hooks = {};
}
// 初始化钩子
init(system) {
// 由子类实现
}
}
// 示例插件
class LoggerPlugin extends Plugin {
constructor() {
super('logger');
this.hooks = {
'beforeRequest': this.beforeRequest.bind(this),
'afterResponse': this.afterResponse.bind(this),
'onError': this.onError.bind(this)
};
}
beforeRequest(request) {
console.log(`[Logger] Request started: ${request.url}`);
return { logged: true, timestamp: Date.now() };
}
afterResponse(response) {
console.log(`[Logger] Response received: ${response.status}`);
return { logged: true, timestamp: Date.now() };
}
onError(error) {
console.error(`[Logger] Error: ${error.message}`);
return { logged: true, timestamp: Date.now() };
}
}
class AnalyticsPlugin extends Plugin {
constructor() {
super('analytics');
this.hooks = {
'afterResponse': this.trackAnalytics.bind(this)
};
this.requests = [];
}
trackAnalytics(response) {
this.requests.push({
url: response.url,
status: response.status,
timestamp: Date.now()
});
console.log(`[Analytics] Tracked request to ${response.url}`);
return { tracked: true, totalRequests: this.requests.length };
}
getStats() {
return {
totalRequests: this.requests.length,
successfulRequests: this.requests.filter(r => r.status === 200).length,
failedRequests: this.requests.filter(r => r.status !== 200).length
};
}
}
// 使用插件系统
console.log('=== 插件系统测试 ===');
const pluginSystem = new PluginSystem();
try {
// 注册插件
const logger = new LoggerPlugin();
const analytics = new AnalyticsPlugin();
pluginSystem.register(logger);
pluginSystem.register(analytics);
// 触发钩子
console.log('触发 beforeRequest 钩子:');
const beforeResults = pluginSystem.trigger('beforeRequest', { url: '/api/data', method: 'GET' });
console.log('Before request results:', beforeResults);
console.log('\n触发 afterResponse 钩子:');
const afterResults = pluginSystem.trigger('afterResponse', { url: '/api/data', status: 200, data: {} });
console.log('After response results:', afterResults);
console.log('\n触发 onError 钩子:');
const errorResults = pluginSystem.trigger('onError', new Error('Network error'));
console.log('Error results:', errorResults);
// 获取插件
console.log('\n插件检查:');
console.log('Has logger plugin:', pluginSystem.hasPlugin('logger'));
console.log('Has analytics plugin:', pluginSystem.hasPlugin('analytics'));
console.log('Has unknown plugin:', pluginSystem.hasPlugin('unknown'));
// 获取统计信息
const analyticsPlugin = pluginSystem.getPlugin('analytics');
if (analyticsPlugin instanceof AnalyticsPlugin) {
console.log('\nAnalytics stats:', analyticsPlugin.getStats());
}
// 错误测试
console.log('\n错误测试:');
try {
pluginSystem.register({}); // 不是 Plugin 实例
} catch (error) {
console.log('注册非插件错误:', error.message);
}
try {
pluginSystem.register(logger); // 重复注册
} catch (error) {
console.log('重复注册错误:', error.message);
}
} catch (error) {
console.error('插件系统错误:', error);
}
八、总结与最佳实践
81 核心要点总结
- new操作符的执行步骤:
- 创建新对象
- 设置原型链
- 绑定 this 值
- 执行构造函数
- 处理返回值
- instanceof的工作原理:
- 沿着原型链向上查找
- 比较构造函数的prototype属性
- 支持Symbol.hasInstance自定义检测
- 原型链的重要性:
- 是实现继承的基础
- 影响 instanceof 的检测结果
- 决定了对象的属性和方法查找路径
8.2 手写实现的关键点
javascript
// new 操作符实现的关键
function myNew(Constructor, ...args) {
// 1. 创建对象并设置原型
const obj = Object.create(Constructor.prototype);
// 2. 执行构造函数
const result = Constructor.apply(obj, args);
// 3. 处理返回值
return result instanceof Object ? result : obj;
}
// instanceof 操作符实现的关键
function myInstanceof(instance, Constructor) {
// 1. 基本类型直接返回 false
if (instance === null || typeof instance !== 'object') {
return false;
}
// 2. 获取原型链
let proto = Object.getPrototypeOf(instance);
const prototype = Constructor.prototype;
// 3. 沿着原型链查找
while (proto !== null) {
if (proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
8.3 最佳实践
- 使用new操作符时:
javascript
// 好的实践
function Person(name) {
if (!(this instanceof Person)) {
return new Person(name); // 防止忘记使用 new
}
this.name = name;
}
// 或者使用 ES6 class
class Person {
constructor(name) {
this.name = name;
}
}
- 使用instanceof时:
javascript
// 好的实践 - 检查类型
function process(value) {
if (value instanceof Array) {
// 处理数组
} else if (value instanceof Date) {
// 处理日期
} else if (value instanceof RegExp) {
// 处理正则表达式
}
}
// 更好的实践 - 使用鸭子类型
function process(value) {
if (Array.isArray(value)) {
// 处理数组
} else if (value instanceof Date || typeof value.getMonth === 'function') {
// 处理日期
}
}
- 避免的问题:
javascript
// 避免的问题1: 修改内置对象的原型
Array.prototype.customMethod = function() {};
const arr = [];
console.log(arr instanceof Array); // true
console.log(arr.customMethod); // function
// 避免的问题2: 跨框架的 instanceof 问题
// 在不同 iframe 中创建的数组,instanceof 可能返回 false
// 避免的问题3: constructor 被修改
function Person() {}
const person = new Person();
person.constructor = Object;
console.log(person instanceof Person); // true
console.log(person.constructor === Person); // false
8.4 性能考虑
- new操作符的性能:
- 原生的
new操作符是最快的 - 手写实现会有轻微性能开销
- 在性能关键路径上使用原生操作
- instanceof的性能:
- 原型链越长,检测越慢
- 对于频繁的类型检测,可以考虑缓存结果
- 使用
typeof检测基本类型更快
- 优化建议:
javascript
// 优化频繁的类型检测
const isArray = Array.isArray;
function processItems(items) {
if (isArray(items)) {
// 处理数组
for (let i = 0; i < items.length; i++) {
// 使用 for 循环而不是 forEach
}
}
}
结语
通过手写new操作符和instanceof操作符,我们深入理解了JavaScript的对象创建机制和类型检测原理。这些知识不仅是面试中的常见考点,更是编写高质量JavaScript代码的基础。
记住以下要点:
- ✅ new 操作符创建对象时,会设置原型链并执行构造函数
- ✅ instanceof 通过检查原型链来判断对象类型
- ✅ 原型链是 JavaScript 继承的基础
- ✅ 理解这些原理有助于避免常见的陷阱和错误
延伸阅读: