授课语音

理解原型和原型链的关系与运用

JavaScript 中的原型和原型链是对象继承和属性查找的核心概念。理解原型和原型链的工作原理是掌握 JavaScript 对象机制的关键。本课将深入解析原型、原型链的定义及其在实际开发中的运用。


1. 什么是原型(Prototype)

在 JavaScript 中,每个对象都有一个属性 prototype,这个属性指向一个对象,我们称这个对象为“原型”。对象的原型对象包含了可以被该对象访问和继承的属性和方法。

1.1 原型的作用

  • 继承:通过原型,JavaScript 实现了对象的继承机制,允许子对象继承父对象的属性和方法。
  • 共享方法和属性:多个实例对象可以共享原型中的属性和方法,从而节省内存。
// 创建一个对象并设置原型
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// 在 Person 的原型上添加一个方法
Person.prototype.sayHello = function() {
  console.log(`你好,我是 ${this.name}`);
};

// 创建 Person 的实例
let person1 = new Person('张三', 25);
person1.sayHello();  // 输出:你好,我是 张三

详细解释:

  • Person.prototype.sayHello 定义了一个方法 sayHello,所有通过 Person 构造函数创建的实例都可以访问这个方法。
  • 即使实例没有定义 sayHello 方法,也可以通过原型继承到这个方法。

2. 原型链(Prototype Chain)

原型链是指对象通过其 prototype 属性形成的链式结构。当访问对象的某个属性时,如果对象本身没有该属性,JavaScript 会继续沿着原型链向上查找,直到找到该属性或达到原型链的顶端 null

2.1 原型链的工作机制

每个对象都有一个内部属性 [[Prototype]],它指向该对象的原型。当访问一个属性时,JavaScript 首先检查对象本身是否有该属性。如果没有,就查找对象的原型,依此类推,直到找到属性或者原型链末端。

// 创建一个对象并设置原型
function Animal(name) {
  this.name = name;
}

Animal.prototype.sayHello = function() {
  console.log(`${this.name} 在叫`);
};

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

// 继承 Animal 的原型
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;  // 修复 constructor 引用

// 在 Dog 的原型上添加一个方法
Dog.prototype.bark = function() {
  console.log(`${this.name} 在叫汪汪`);
};

// 创建 Dog 的实例
let dog = new Dog('小狗', '比熊');
dog.sayHello();  // 输出:小狗 在叫
dog.bark();  // 输出:小狗 在叫汪汪

详细解释:

  • Dog.prototype = Object.create(Animal.prototype)Dog 的原型设置为 Animal 的原型,这样 Dog 就能继承 Animal 的方法。
  • 在创建 dog 实例时,首先会查找 dog 是否有 sayHello 方法,若没有,则会沿着原型链查找,最终在 Animal.prototype 中找到该方法。

3. 原型链的继承关系

通过原型链,JavaScript 实现了继承的机制。在继承中,子类对象可以访问父类对象的方法和属性。原型链的继承关系可以通过构造函数的 prototype 属性来实现。

3.1 继承的两种方式

  • 原型链继承:子类的原型指向父类的实例,继承父类的方法和属性。
  • 构造函数继承:通过 callapply 方法将父类构造函数的属性和方法继承到子类实例中。
// 原型链继承的例子
function Animal(name) {
  this.name = name;
}

Animal.prototype.sayHello = function() {
  console.log(`${this.name} 在叫`);
};

function Dog(name, breed) {
  Animal.call(this, name);  // 使用构造函数继承
  this.breed = breed;
}

// 使用原型链继承父类的方法
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

// 在 Dog 的原型上添加方法
Dog.prototype.bark = function() {
  console.log(`${this.name} 在叫汪汪`);
};

let dog = new Dog('小狗', '哈士奇');
dog.sayHello();  // 输出:小狗 在叫
dog.bark();  // 输出:小狗 在叫汪汪

详细解释:

  • Dog.prototype = Object.create(Animal.prototype) 实现了 Dog 对象继承了 Animal 对象的原型。
  • Animal.call(this, name) 使用构造函数的 call 方法将 Animal 构造函数中的属性赋值给 Dog 实例。

4. 原型与对象的继承关系图解

在 JavaScript 中,所有对象的原型最终都会继承自 Object.prototype,这是原型链的顶端。以下是对象的继承关系示意图:

对象实例 → 构造函数.prototype → Object.prototype → null

4.1 具体例子:

let obj = { name: '张三' };
console.log(obj.toString());  // 输出:[object Object]

详细解释:

  • obj 作为一个普通对象,它的原型链最终会查找到 Object.prototype,因此它能继承 Object.prototype 中的 toString 方法。
  • Object.prototype 是所有对象的顶级原型。

5. 总结

  • 原型:每个对象都有一个原型对象,原型对象包含了该对象可以继承的属性和方法。
  • 原型链:原型链是对象之间通过原型关联形成的链式结构。通过原型链查找属性和方法。
  • 原型继承:JavaScript 中的继承机制通过原型链实现,子类可以继承父类的方法和属性。
  • 构造函数与原型链结合:通过构造函数和原型链结合,可以实现 JavaScript 的继承机制,避免重复代码,提高代码复用性。

掌握原型和原型链的机制,是理解 JavaScript 面向对象编程的关键。

去1:1私密咨询

系列课程: