目录
一、核心概念解析
显式原型 (prototype):
是函数特有的属性
当函数作为构造函数使用时,其实例会继承该函数的prototype
本质是一个包含
constructor
属性和共享方法的普通对象function Person(name) { this.name = name; } // 向显式原型添加方法 Person.prototype.sayHello = function() { console.log(`Hello, I'm ${this.name}`); }; // 验证类型 console.log(typeof Person.prototype); // "object" console.log(Person.prototype.constructor === Person); // true
隐式原型 (proto):
是每个对象都拥有的属性
指向创建该对象时使用的构造函数的prototype
形成原型链的关键连接点(ES6后建议使用
Object.getPrototypeOf()
获取)const john = new Person("John"); // 对象通过__proto__访问构造函数的原型 console.log(john.__proto__ === Person.prototype); // true // 推荐的标准获取方式 console.log(Object.getPrototypeOf(john) === Person.prototype); // true
二、原型链运作机制
[实例对象] john │ ├── __proto__ → [Person.prototype] │ │ │ ├── sayHello() │ │ │ └── __proto__ → [Object.prototype] │ │ │ ├── toString() │ ├── valueOf() │ │ │ └── __proto__ → null │ └── name: "John"
原型链查找规则:
访问对象属性时,先在自身查找
找不到则通过
__proto__
向上级原型查找直到Object.prototype(所有对象的基类)
最终到达null停止查找
console.log(john.toString()); // 来自Object.prototype console.log(john.sayHello()); // 来自Person.prototype // 验证原型链关系 console.log(john instanceof Person); // true console.log(john instanceof Object); // true
三、构造函数的原型三角关系
function Animal(type) {
this.type = type;
}
const dog = new Animal("哺乳动物");
// 经典三角关系:
console.log(dog.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.constructor === Animal); // true
console.log(Object.getPrototypeOf(Animal) === Function.prototype); // true
四、ES6类语法糖的本质
class Vehicle {
constructor(wheels) {
this.wheels = wheels;
}
drive() {
console.log(`Driving with ${this.wheels} wheels`);
}
}
// 类本质仍是构造函数
console.log(typeof Vehicle); // "function"
const car = new Vehicle(4);
// 验证原型关系
console.log(car.__proto__ === Vehicle.prototype); // true
console.log(Vehicle.prototype.__proto__ === Object.prototype); // true
五、实现原型继承的三种方式
组合继承(最经典方式):
function Parent(name) { this.name = name; } Parent.prototype.speak = function() { console.log(`I'm ${this.name}`); }; function Child(name, age) { Parent.call(this, name); // 继承属性 this.age = age; } // 建立原型链 Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; // 添加子类方法 Child.prototype.cry = function() { console.log("Waahh!"); };
ES6类继承:
class Employee { constructor(name) { this.name = name; } work() { console.log(`${this.name} is working`); } } class Manager extends Employee { constructor(name, teamSize) { super(name); this.teamSize = teamSize; } manage() { console.log(`Managing ${this.teamSize} people`); } }
Object.create实现纯对象继承:
const parent = { greet() { console.log("Hello from parent!"); } }; const child = Object.create(parent, { cry: { value: function() { console.log("Waaaah!"); } } });
六、关键注意事项
原型污染风险:
// 修改内置原型需谨慎 Array.prototype.sum = function() { return this.reduce((a, b) => a + b, 0); }; const nums = [1, 2, 3]; console.log(nums.sum()); // 6 - 但可能引发冲突
性能优化技巧:
// 在构造函数中声明方法(适用于不共享的独特方法) function Player(id) { this.id = id; // 每个实例都有独立方法副本 this.getID = function() { return this.id; }; } // 在原型上声明方法(节省内存) Player.prototype.getScore = function() { return this.score; };
现代替代方案:
使用
Object.getPrototypeOf()
替代__proto__
使用
Object.setPrototypeOf()
设置原型ES6的
Reflect
API提供更多元操作
七、原型链应用场景
实现混入模式(Mixins):
const canEat = { eat() { console.log(`${this.name} is eating`); } }; const canSleep = { sleep() { console.log(`${this.name} is sleeping`); } }; function assignPrototypes(target, ...sources) { Object.assign(target.prototype, ...sources); } function Cat(name) { this.name = name; } assignPrototypes(Cat, canEat, canSleep); const garfield = new Cat("Garfield"); garfield.eat(); // "Garfield is eating"
八、总结核心要点
显式原型(prototype):函数的属性,用于实现方法共享
隐式原型(proto):对象的内部链接,指向其构造函数的prototype
原型链:通过
__proto__
连接形成的继承链最佳实践:
方法定义在prototype上节省内存
属性定义在构造函数中保持独立
避免直接修改内置对象原型
优先使用ES6类语法提高可读性