ZDecode
Javascript

原型/原型链

一、基本概念

  1. prototype(构造函数的原型对象)

    • 每个 函数(Function) 在创建时,默认会有一个名为 prototype 的属性,指向一个对象。

    • 这个对象会成为 由该函数作为构造器 new 出来的对象的原型。

    • 示例:

    function Person() {}
    console.log(Person.prototype); // { constructor: f Person, ... }
  2. __proto__(对象的隐式原型,非标准但广泛实现)

    • 所有 对象 都有一个 __proto__ 属性(大多数现代浏览器都支持),它指向该对象的原型对象(即构造函数的 prototype)。

    • 它实际上是 [[Prototype]] 的一个访问器,是浏览器为开发者调试提供的便利方式。

    const p = new Person();
    console.log(p.__proto__ === Person.prototype); // true
  3. [[Prototype]](内部槽) 是 对象内部的隐藏属性,由规范定义。

    • 不能直接访问,但可以通过 __proto__Object.getPrototypeOf() 读到,也可以用 Object.setPrototypeOf() 修改。

    • 真正控制了“原型链”行为的是 [[Prototype]]

二、原型与原型链

什么是原型?

  • 在 JS 中,函数可以作为构造函数,new 关键字创建出来的对象会将 [[Prototype]] 设置为该函数的 prototype。

  • 每个对象都有一个原型(即 [[Prototype]]),这个原型本身也是一个对象。

原型链的定义

  • 当访问对象的属性时,如果对象本身没有,就会去它的原型([[Prototype]])中查找;

  • 如果还找不到,就去原型的原型查找,一直向上查;

  • 直到原型为 null(即 Object.prototype.__proto__ === null)为止;

  • 这一串查找的过程称为原型链(Prototype Chain)。

示例说明:

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

const p1 = new Person('Alice');

// 查找 sayHello 属性过程:
p1.sayHello(); // => 会先查找 p1 上有没有 sayHello 属性
              // => 没有,往 p1.__proto__(即 Person.prototype)上找
              // => 找到 sayHello,执行

三个对象的关系总结图(结构)

p1 --> [[Prototype]] --> Person.prototype --> [[Prototype]] --> Object.prototype --> null

Person --> prototype --> Person.prototype
function Animal() {}
Animal.prototype.eat = function () {
  console.log("eating");
};

function Dog() {}
Dog.prototype = new Animal(); // 原型继承
Dog.prototype.constructor = Dog;

const d = new Dog();
d.eat(); // 继承自 Animal.prototype

三、es5 中的继承方式

  1. 原型链继承(Prototype Chain Inheritance)

    function Parent() {
      this.name = 'parent';
    }
    Parent.prototype.sayHi = function () {
      console.log('Hi from parent');
    };
    
    function Child() {}
    Child.prototype = new Parent(); // 核心
    
    const c = new Child();
    c.sayHi(); // Hi from parent
    • 优点:

      • 简单易懂,父类方法可复用。
    • 缺点:

      • 所有子类实例共享父类引用属性(如数组会互相影响)。
      • 子类构造函数无法向父类传参。
  2. 借用构造函数继承(Constructor Stealing / Classic Inheritance

function Parent(name) {
  this.name = name;
  this.arr = [1, 2];
}
function Child(name) {
  Parent.call(this, name); // 核心:借用父构造函数
}

const c1 = new Child('Tom');
const c2 = new Child('Jerry');
c1.arr.push(3);
console.log(c2.arr); // [1, 2]
  • 优点:

    • 避免引用属性共享问题。

    • 可向父类传参。

  • 缺点:

    • 方法不能复用(每个实例都有一份父类方法)。
  1. 组合继承(Combination Inheritance)⭐最常用

    function Parent(name) {
      this.name = name;
      this.arr = [1, 2];
    }
    Parent.prototype.sayHi = function () {
      console.log('Hi, ' + this.name);
    };
    
    function Child(name) {
      Parent.call(this, name); // 借用构造函数
    }
    Child.prototype = new Parent(); // 原型链继承
    Child.prototype.constructor = Child;
    
    const c = new Child('Tom');
    c.sayHi(); // Hi, Tom
    • 优点:

      • 实例独立、方法可复用。
    • 缺点:

      • 调用了两次父构造函数(一次在 Child.prototype = new Parent(),一次在 Parent.call(this))。
  2. 原型式继承(Object.create 简化版)

    const parent = {
      name: 'parent',
      arr: [1, 2],
    };
    const child = Object.create(parent);
    child.name = 'child';
    • 优点:

      • 创建对象很方便。
    • 缺点:

      • 共享引用属性。

      • 无法传参。

  3. 寄生式继承(Parasitic Inheritance

    function createChild(obj) {
      const clone = Object.create(obj);
      clone.sayHi = function () {
        console.log('Hi');
      };
      return clone;
    }
    const parent = { name: 'parent' };
    const child = createChild(parent);
    • 优点:

      • 可以扩展功能。
    • 缺点:

      • 方法不可复用(每次都新建方法)。
  4. 寄生组合式继承(Parasitic Combination Inheritance)⭐推荐使用

    function Parent(name) {
      this.name = name;
    }
    Parent.prototype.sayHi = function () {
      console.log('Hi, ' + this.name);
    };
    function Child(name) {
      Parent.call(this, name);
    }
    Child.prototype = Object.create(Parent.prototype); // 不执行 Parent 构造函数
    Child.prototype.constructor = Child;
    • 优点:

      • 避免重复调用构造函数。

      • 方法共享、实例独立,性能最好。

继承方式可传参共享引用属性问题方法复用性能
原型链继承
构造函数继承
组合继承一般
原型式继承
寄生式继承
寄生组合继承最优