深入理解 JavaScript 原型与原型链

178 阅读1分钟

深入理解 JavaScript 原型与原型链

一、原型(Prototype):一切皆对象的起点

在 JavaScript 中,每个对象都有一个隐藏的 [[Prototype]] 属性(现代浏览器可通过 __proto__ 访问),这是 JS 实现继承的基石。当你创建一个新对象时,它会自动关联一个原型对象,形成继承关系链。

代码示例:

// 构造函数
function Person(name) {
    this.name = name;
}

// 给原型添加方法
Person.prototype.greet = function() {
    console.log(`Hello, I'm ${this.name}`);
}

const alice = new Person("Alice");
alice.greet(); // 调用原型方法

原型示意图:

QQ20250209-045847@2x.png

二、原型链:层层递进的继承网络

当访问对象属性时,JS 引擎会执行以下搜索:

  1. 检查对象自身属性
  2. 沿 __proto__ 向上查找原型对象
  3. 直到 Object.prototype 停止(其 __proto__ 为 null)

原型链验证:

console.log(alice.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null

原型链示意图:

QQ20250209-051303@2x.png

三、核心概念对比

特性__proto__prototype
归属对象所有对象仅函数对象
标准访问方式Object.getPrototypeOf(obj)函数默认属性
作用实现原型继承作为构造函数创建实例的原型

四、实现继承的演进之路

1. 组合继承(经典模式)

function Animal(name) {
    this.name = name;
}

Animal.prototype.eat = function() {
    console.log(`${this.name} is eating`);
}

function Dog(name, breed) {
    Animal.call(this, name); // 继承属性
    this.breed = breed;
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
    console.log("Woof!");
}

2. 现代 class 语法(ES6+)

class Animal {
    constructor(name) {
        this.name = name;
    }

    eat() {
        console.log(`${this.name} is eating`);
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name);
        this.breed = breed;
    }

    bark() {
        console.log("Woof!");
    }
}

五、关键注意事项

  1. 原型修改时机:在创建实例后修改原型会影响所有实例
  2. 性能优化:过长的原型链会影响查找效率
  3. 属性屏蔽:实例属性会覆盖原型同名属性
  4. 现代实践:推荐使用 Object.create() 代替直接修改 __proto__

六、调试技巧

// 检查原型链
console.log(alice instanceof Person); // true

// 查看对象自身属性
console.log(Object.keys(alice)); // ["name"]

// 检测属性来源
console.log(alice.hasOwnProperty('greet')); // false
console.log('greet' in alice); // true

七、总结与展望

理解原型链机制是掌握 JavaScript 面向对象编程的关键。虽然现代 ES6 class 语法让继承更直观,但其底层仍然是基于原型链实现。