探索 JavaScript 中的原型与原型链

120 阅读3分钟

JavaScript 原型与原型链分析

在 JavaScript 中,每个对象都有一个内建的属性 prototype,这个属性指向对象的原型。原型本身也是一个对象,而原型对象又有一个 prototype 属性,这样形成了一个原型链。JavaScript 的原型链机制使得我们能够通过一个对象访问到它的继承属性。

1. 原型(Prototype)

每个函数在 JavaScript 中都有一个 prototype 属性,这个属性指向该函数的原型对象。类实例通过原型对象来继承方法和属性。简单来说,原型是通过构造函数创建的对象,它包含了实例对象可以访问的属性和方法。

示例:

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

Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}`);
};

const john = new Person("John", 30);
john.sayHello();  // Output: Hello, my name is John

在这个示例中,Person 函数是构造函数,Person.prototype 是原型对象,sayHello 方法定义在 Person.prototype 上,因此 john 实例能够访问该方法。

2. 原型链(Prototype Chain)

原型链是由对象的原型对象连接起来的链条。每当访问对象的属性时,JavaScript 会首先在该对象中查找,如果没有找到,便会向上查找对象的原型。如果原型对象没有该属性,JavaScript 会继续向上查找,直到 Object.prototype 为止。

原型链的查找过程是:

  1. 查找实例对象是否有该属性。
  2. 如果没有,查找该对象的原型(即构造函数的 prototype)。
  3. 如果还没有,继续查找原型的原型,直到 Object.prototype

示例:

    function Animal() {
      this.species = "Animal";
    }

    Animal.prototype.sayHello = function() {
      console.log("Hello from Animal");
    };

    function Dog() {
      this.breed = "Golden Retriever";
    }

    Dog.prototype = new Animal();  // Dog 继承 Animal
    
    // 新增,重写 Dog.prototype 的 constructor 属性,指向自己的构造函数 Dog
    Dog.prototype.constructor = Dog
    
    const dog = new Dog();
    
    console.log(dog.species);  // Output: Animal
    dog.sayHello();  // Output: Hello from Animal

在这个示例中,Dog 通过 Dog.prototype = new Animal() 继承了 Animal 的属性和方法。当我们访问 dog.species 时,Dog 实例没有该属性,JavaScript 会沿着原型链查找,最终在 Animal.prototype 上找到 species 属性。

以下是原型链的示意图,帮助你更好地理解原型链的结构:

原型.png

3. 原型链的终点:Object.prototype

Object.prototype 是所有对象的原型链的终点。当查找属性时,如果一直没有找到,则会返回 undefined。因此,Object.prototype 是原型链的根。

    const obj = {};
    console.log(obj.toString());  // Output: [object Object]

在上面的例子中,obj 是一个空对象,它没有自己的 toString 方法,JavaScript 会沿着原型链查找,最终在 Object.prototype 中找到 toString 方法。

4. 原型链的作用

原型链提供了继承和方法共享的机制,使得不同的对象可以共享同一个方法和属性。这样不仅节省了内存空间,也使得代码更加灵活和可复用。

5. 原型和构造函数

原型和构造函数是密切相关的。每个函数都有一个 prototype 属性,当我们通过构造函数创建对象时,新创建的对象的 __proto__ 属性会指向构造函数的 prototype

示例:

    function Car(model) {
      this.model = model;
    }

    const car1 = new Car("Toyota");
    console.log(car1.__proto__ === Car.prototype);  // Output: true

在这个示例中,car1.__proto__ 指向 Car.prototype,这是构造函数和原型之间的关联。

结论

JavaScript 的原型和原型链是实现继承的核心机制。通过原型链,我们可以访问和继承对象的属性和方法,进而实现代码复用。理解原型和原型链的概念对于深入掌握 JavaScript 的继承机制和面向对象编程非常重要。