JS 原型与原型链:从构造函数到对象继承的完整解析

38 阅读3分钟

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 遵循“就近原则”:

  1. 先在自身属性中查找;
  2. 若未找到,则沿 __proto__ 向上查找;
  3. 直到 Object.prototype
  4. 最终到达 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 背后的运行原理,不再被“八股文”困扰。