跳转至

高级概念:原型与继承

在 JavaScript 中,原型(Prototype)和继承(Inheritance)是理解对象如何共享属性和方法的关键概念。JavaScript 是一种基于原型的语言,这意味着对象可以直接从其他对象继承属性和方法,而不是通过传统的类继承机制。本章将深入探讨 JavaScript 中的原型链和继承机制。

1. 原型链

1.1 什么是原型?

在 JavaScript 中,每个对象都有一个内部属性 [[Prototype]],通常通过 __proto__ 属性访问。这个属性指向该对象的原型对象。原型对象本身也是一个对象,因此它也有自己的原型,这样就形成了一个原型链。

1.2 原型链的工作原理

当你试图访问一个对象的属性或方法时,JavaScript 引擎会首先在该对象本身查找。如果找不到,它会沿着原型链向上查找,直到找到该属性或方法,或者到达原型链的末端(null)。

1.3 示例:原型链的基本用法

// 创建一个简单的对象
let animal = {
  eats: true
};

// 创建一个新对象,并将其原型设置为 animal
let rabbit = Object.create(animal);

console.log(rabbit.eats); // true,因为 rabbit 继承了 animal 的 eats 属性

在这个例子中,rabbit 对象通过 Object.create(animal) 继承了 animal 对象的 eats 属性。当我们访问 rabbit.eats 时,JavaScript 引擎首先在 rabbit 对象中查找 eats 属性,发现没有,于是沿着原型链向上查找,最终在 animal 对象中找到了 eats 属性。

2. 构造函数与原型

2.1 构造函数

在 JavaScript 中,构造函数是用来创建对象的函数。通过 new 关键字调用构造函数时,会创建一个新对象,并将该对象的原型设置为构造函数的 prototype 属性。

2.2 示例:使用构造函数创建对象

// 定义一个构造函数
function Animal(name) {
  this.name = name;
}

// 在构造函数的原型上添加方法
Animal.prototype.speak = function() {
  console.log(this.name + ' makes a noise.');
};

// 创建一个新对象
let dog = new Animal('Dog');
dog.speak(); // Dog makes a noise.

在这个例子中,Animal 是一个构造函数,我们通过 new Animal('Dog') 创建了一个新对象 dogdog 对象的原型是 Animal.prototype,因此它可以访问 Animal.prototype 上的 speak 方法。

3. 继承的实现

3.1 原型继承

在 JavaScript 中,继承通常通过原型链来实现。我们可以通过将一个构造函数的原型设置为另一个构造函数的实例来实现继承。

3.2 示例:原型继承

// 定义一个父类构造函数
function Animal(name) {
  this.name = name;
}

// 在父类的原型上添加方法
Animal.prototype.speak = function() {
  console.log(this.name + ' makes a noise.');
};

// 定义一个子类构造函数
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(this.name + ' barks.');
};

// 创建一个新对象
let myDog = new Dog('Rex', 'German Shepherd');
myDog.speak(); // Rex makes a noise.
myDog.bark();  // Rex barks.

在这个例子中,Dog 继承了 Animal 的属性和方法。我们通过 Object.create(Animal.prototype)Dog.prototype 设置为 Animal.prototype 的一个实例,从而实现了原型继承。

4. 练习题

4.1 简单练习

  1. 创建一个 Person 构造函数,包含 nameage 属性。在 Person.prototype 上添加一个 greet 方法,打印出 Hello, my name is [name]

4.2 中等练习

  1. 创建一个 Student 构造函数,继承自 PersonStudent 应该有一个额外的 major 属性。在 Student.prototype 上添加一个 study 方法,打印出 [name] is studying [major]

4.3 复杂练习

  1. 创建一个 Professor 构造函数,继承自 PersonProfessor 应该有一个额外的 department 属性和一个 teach 方法,打印出 [name] is teaching in the [department] department。然后创建一个 Course 构造函数,包含 nameprofessor 属性。在 Course.prototype 上添加一个 describe 方法,打印出 This course is [name], taught by [professor.name]

5. 总结

  • 原型链:JavaScript 中的对象通过原型链继承属性和方法。每个对象都有一个 [[Prototype]] 属性,指向它的原型对象。
  • 构造函数:通过构造函数可以创建对象,并将对象的原型设置为构造函数的 prototype 属性。
  • 原型继承:通过将一个构造函数的原型设置为另一个构造函数的实例,可以实现继承。
  • 继承的实现:在 JavaScript 中,继承通常通过原型链来实现,可以通过 Object.create()call() 方法来实现继承。

理解原型和继承是掌握 JavaScript 面向对象编程的关键。通过原型链,JavaScript 提供了一种灵活且强大的机制来实现对象的继承和共享行为。