Skip to content

06. JavaScript 原型链原理详解

1. 构造函数与原型对象的关系

我们知道,对于 JS 创建对象有两种方式:

  1. 过程式创建,就像上面一样,使用 {} 包围键值对来创建
  2. 面向对象编程,使用包含 this 的函数作为类创建器

对于 ES6+,class 语法糖可以代替上述面向对象的创建方式,这种方式的本质仍然是第二种方式。

设函数为 f,由此函数 new 所创建的三个对象 a1a2a3

  • prototype 是每个函数都拥有的属性,这个函数创建对象时将使用这个对象作为原型
  • __proto__constructora1a2a3 所拥有的属性
    • __proto__ 指向对象的原型对象,原型对象通常和被 new 的对象结构一致
    • constructor 指向构造该对象的函数
  • a1.constructor === f
  • a1.__proto__ === f.prototype

每个 JavaScript 函数(箭头函数除外)在创建时都会自动获得一个 prototype 属性,该属性指向一个包含 constructor 属性的对象。这种设计实现了实例共享方法的能力。

javascript
function Person(name) {
  this.name = name;
}

// 自动创建的原型对象
console.log(Person.prototype.constructor === Person); // true

当使用 new 操作符创建实例时:

javascript
const alice = new Person('Alice');
console.log(alice.__proto__ === Person.prototype); // true

2. 原型链查找机制

属性访问遵循委托查找规则

  1. 在实例自身属性中查找
  2. 未找到时沿 __proto__ 链向上查找
  3. 直到 Object.prototype(最终为 null

示例演示:

javascript
Person.prototype.sayHello = function() {
  console.log(`Hello, I'm ${this.name}`);
};

alice.sayHello(); // 通过原型链调用
console.log(alice.toString()); // 来自 Object.prototype

3. 原型链终点验证

通过递归访问 __proto__ 可验证原型链终点:

javascript
function getProtoChain(obj) {
  const chain = [];
  let current = obj;

  while(current) {
    chain.push(current.constructor.name);
    current = Object.getPrototypeOf(current);
  }
  return chain;
}

console.log(getProtoChain(alice));
// ["Person", "Object", ...]

4. 原型继承的实现模式

4.1 组合继承(经典继承)

javascript
function Student(name, grade) {
  Person.call(this, name);
  this.grade = grade;
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

const bob = new Student('Bob', 10);
console.log(bob instanceof Person); // true

4.2 ES6 Class 语法糖

javascript
class Teacher extends Person {
  constructor(name, subject) {
    super(name);
    this.subject = subject;
  }
}

const mrSmith = new Teacher('Smith', 'Math');
console.log(mrSmith.sayHello()); // 继承自 Person

5. 关键方法对比

方法作用域原型链检查
hasOwnProperty仅自身属性×
in 操作符全部属性
Object.keys()可枚举属性×
Reflect.ownKeys()所有属性×
javascript
console.log('toString' in alice); // true
console.log(alice.hasOwnProperty('toString')); // false

6. 性能优化建议

  1. 避免过长的原型链:保持继承层级在 3-4 级以内
  2. 谨慎扩展原生原型:可能引发命名冲突
  3. 优先使用对象组合:而非深层次类继承
javascript
// 不推荐的写法
Array.prototype.customMethod = function() { /*...*/ };

// 建议的替代方案
const arrayUtils = {
  customMethod: function(arr) { /*...*/ }
};

7. 现代 JavaScript 的改进

使用 Object.create() 实现纯净原型链:

javascript
const animal = {
  eat() {
    console.log('Eating...');
  }
};

const rabbit = Object.create(animal, {
  jump: { value: function() { /*...*/ } }
});

通过原型链理解,我们可以更好地掌握 JavaScript 的面向对象特性,在框架开发、库设计和大型应用架构中做出合理的设计决策。