一、核心概念
每个对象(Object)都有一个私有属性指向另一个名为原型(prototype)的对象。原型对象也有一个自己的原型,层层向上直到一个对象的原型为 null
。根据定义,null
没有原型,并作为这个原型链(prototype chain)中的最后一个环节。
prototype属性:每个函数都有的属性,指向原型对象
__proto__属性:每个实例对象都有的属性,指向构造函数的原型
constructor属性:原型对象指向构造函数的引用
二、原型链验证方法
function Person(name) {
this.name = name;
}
const jack = new Person('Jack');
// 验证原型关系
console.log(jack.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
// 原型链检测
console.log(jack instanceof Person); // true
console.log(jack instanceof Object); // true
console.log(Person.prototype.isPrototypeOf(jack)); // true
三、原型链查找机制
function Animal() {
this.type = '生物';
}
Animal.prototype.breathe = function() {
return "正在呼吸";
};
function Dog() {
this.sound = '汪汪';
}
// 设置原型继承
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
const myDog = new Dog();
console.log(myDog.breathe()); // 通过原型链查找到Animal原型的方法
console.log(myDog.hasOwnProperty('breathe')); // false
四、完整的原型链结构
实例对象
myDog
Dog.prototype(包含Animal实例)
Animal.prototype
Object.prototype
null
五、如何实现继承
// 父类
function Vehicle(wheels) {
this.wheels = wheels || 0;
}
Vehicle.prototype.move = function() {
return `正在移动,有 ${this.wheels} 个轮子`;
};
// 子类
function Car() {
Vehicle.call(this, 4); // 继承实例属性
this.brand = '未知';
}
// 设置原型链
Car.prototype = Object.create(Vehicle.prototype);
Car.prototype.constructor = Car;
// 添加子类方法
Car.prototype.honk = function() {
return '嘟嘟!';
};
const myCar = new Car();
console.log(myCar.move()); // 继承父类方法
六、ES6 class语法糖
class Shape {
constructor(color) {
this.color = color;
}
draw() {
return `绘制${this.color}的图形`;
}
}
class Circle extends Shape {
constructor(color, radius) {
super(color);
this.radius = radius;
}
area() {
return Math.PI * this.radius ** 2;
}
}
const redCircle = new Circle('red', 5);
console.log(redCircle.draw()); // 继承父类方法
七、注意事项
1.原型污染问题:修改内置对象原型需谨慎
// 不推荐的做法
Array.prototype.sum = function() {
return this.reduce((a, b) => a + b, 0);
};
2.性能优化:过长的原型链会影响查找效率
3.对象创建方式:
Object.create()
直接指定原型new
操作符创建实例
4.现代替代方案:考虑使用组合代替继承
八、调试技巧
使用控制台查看原型链:
console.dir(myCar);
检查属性来源:
console.log(Object.getOwnPropertyNames(myCar));
console.log(myCar.hasOwnProperty('wheels'));
总结
1.优先使用ES6 class语法
2.避免超过3层的原型链继承
3.使用组合模式替代复杂继承
4.始终在继承时修正constructor指向