JS 原型与原型链:从构造函数到对象继承的完整解析
在 JavaScript 面试中,“原型”“原型链”“__proto__”“prototype”几乎是必考内容。即使 ES6 引入了 class 语法糖,其底层依然基于 ES5 的原型机制。本文将通过清晰结构和典型示例,带你彻底理解 JavaScript 的原型系统。
一、为什么需要原型?
ES5 没有类的概念,只能通过构造函数 + 原型模拟面向对象。
- 构造函数:定义实例私有属性
- 原型(
prototype) :存放所有实例共享的方法和属性
function Car(color) {
this.color = color; // 实例属性
}
Car.prototype.name = '小米SU7';
Car.prototype.drive = function() {
console.log('drive, 下赛道');
};
const car1 = new Car('霞光紫');
const car2 = new Car('星空蓝');
console.log(car1.name); // 共享属性
car1.drive(); // 共享方法
这样既节省内存,又实现代码复用。
二、构造函数、prototype 与实例的关系
每个函数都有一个 prototype 属性,指向一个对象(即“原型对象”)。
每个通过 new 创建的实例,内部都有一个隐式链接 __proto__,指向其构造函数的 prototype。
function Person(name) {
this.name = name;
}
Person.prototype.species = '人类';
const p1 = new Person('马冬梅');
console.log(p1.__proto__ === Person.prototype); // true
✅ 关键关系:
实例.__proto__ === 构造函数.prototype
三、constructor:原型指回构造函数
原型对象默认包含一个 constructor 属性,指回其构造函数:
console.log(Person.prototype.constructor === Person); // true
但若整体重写 prototype,会丢失该引用:
Person.prototype = {
sayHello() { console.log(`你好,我是${this.name}`); }
};
// 此时 Person.prototype.constructor 指向 Object!
// 修复方式:
Person.prototype.constructor = Person;
四、属性查找:就近原则与原型链
访问对象属性时,JavaScript 遵循“就近原则”:
- 先在自身属性中查找;
- 若未找到,则沿
__proto__向上查找; - 直到
Object.prototype; - 最终到达
null,停止查找。
const li = new Person('荔枝');
li.species = '瓦学弟'; // 覆盖实例属性,不影响原型
console.log(li.species); // "瓦学弟"(自身)
console.log(li.toString()); // 来自 Object.prototype
这就是原型链的工作机制。
五、原型链的终点:Object.prototype
所有普通对象最终都继承自 Object.prototype:
const obj = { name: '荔枝' };
console.log(obj.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
null 是原型链的终点,表示“无处可查”。
六、实现继承:基于原型链
通过设置子类的 prototype 为父类的实例,可实现继承:
function Animal() {}
Animal.prototype.species = '动物';
function Person() {}
Person.prototype = new Animal(); // 继承 Animal
Person.prototype.constructor = Person;
const pp = new Person();
console.log(pp.species); // "动物"
console.log(pp.toString()); // 来自 Object.prototype
查找路径为:
pp → Person.prototype → Animal.prototype → Object.prototype → null
七、JS 面向对象的本质:委托,而非血缘
传统 OOP(如 Java)是“类生实例”的血缘继承;
而 JavaScript 是对象委托:实例通过 __proto__ 向原型“借用”能力。
- 每个函数有
prototype - 每个对象有
__proto__ - 所有对象通过
__proto__串成一条原型链
这正是 JavaScript 灵活又强大的根源。
总结
- 构造函数定义实例属性,
prototype存放共享方法。 - 实例的
__proto__指向构造函数的prototype。 - 属性查找沿原型链向上,直到
Object.prototype,终点为null。 - 继承通过设置
prototype实现。 - JS 的面向对象本质是基于委托的原型链机制,而非类继承。
掌握这套机制,你就能真正理解 class 背后的运行原理,不再被“八股文”困扰。