← 返回首页

JavaScript 原型链与继承

JavaScript 是基于原型的语言,理解原型链是掌握 JavaScript 继承机制的关键。本文将带你深入理解原型链的工作原理和现代类语法。

1. 什么是原型?

每个 JavaScript 对象都有一个内部属性 [[Prototype]](可通过 __proto__Object.getPrototypeOf() 访问),指向另一个对象,这个对象就是它的原型。

const obj = { name: "皮皮" };
const proto = Object.getPrototypeOf(obj);
console.log(proto === Object.prototype); // true

2. 原型链的工作原理

当访问对象的属性时,JavaScript 会:

  1. 首先在对象本身查找
  2. 如果找不到,沿着原型链向上查找
  3. 直到找到该属性或到达原型链末端(null)
function Person(name) {
    this.name = name;
}

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

const p = new Person("Tom");
console.log(p.name);        // "Tom" - 实例属性
console.log(p.sayHello());  // "Hello, I'm Tom" - 原型方法

3. 构造函数与 new

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.introduce = function() {
    return `${this.name}, ${this.age} years old`;
};

const p1 = new Person("Tom", 25);
const p2 = new Person("Jerry", 30);

console.log(p1.constructor === Person); // true
console.log(p1.__proto__ === Person.prototype); // true

4. ES6 Class 语法

ES6 引入了 class 语法,让继承更加直观:

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    
    introduce() {
        return `${this.name}, ${this.age} years old`;
    }
}

class Developer extends Person {
    constructor(name, age, language) {
        super(name, age);
        this.language = language;
    }
    
    code() {
        return `${this.name} codes in ${this.language}`;
    }
}

const dev = new Developer("Tom", 25, "JavaScript");
console.log(dev.introduce()); // "Tom, 25 years old"
console.log(dev.code());      // "Tom codes in JavaScript"

5. 原型链图示

dev (实例)
  ↓ __proto__
Developer.prototype
  ↓ __proto__
Person.prototype
  ↓ __proto__
Object.prototype
  ↓ __proto__
null (原型链终点)

6. 继承的多种方式

原型链继承

function Parent() {
    this.colors = ["red", "blue"];
}

function Child() {}
Child.prototype = new Parent();

const c1 = new Child();
const c2 = new Child();
c1.colors.push("green");
console.log(c2.colors); // ["red", "blue", "green"] - 引用共享!

构造函数继承

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

function Child(name, age) {
    Parent.call(this, name); // 调用父类构造函数
    this.age = age;
}

组合继承(最常用)

function Parent(name) {
    this.name = name;
}
Parent.prototype.sayName = function() {
    console.log(this.name);
};

function Child(name, age) {
    Parent.call(this, name); // 继承属性
    this.age = age;
}
Child.prototype = new Parent(); // 继承方法
Child.prototype.constructor = Child;
现代建议: 优先使用 ES6 class 语法,代码更清晰,底层仍然是原型继承。

7. 实用技巧

判断继承关系

const obj = new Person("Tom");
console.log(obj instanceof Person);  // true
console.log(obj instanceof Object);  // true
console.log(Person.prototype.isPrototypeOf(obj)); // true

检查自有属性

const obj = { name: "Tom" };
console.log(obj.hasOwnProperty("name")); // true
console.log(obj.hasOwnProperty("toString")); // false - 来自原型

总结

原型链是 JavaScript 继承的核心机制。理解原型链不仅有助于掌握传统继承模式,也能更好地理解 ES6 class 的本质。无论是使用构造函数还是 class 语法,底层都是原型继承。