高级概念:原型与继承¶
在 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')
创建了一个新对象 dog
。dog
对象的原型是 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 简单练习¶
- 创建一个
Person
构造函数,包含name
和age
属性。在Person.prototype
上添加一个greet
方法,打印出Hello, my name is [name]
。
4.2 中等练习¶
- 创建一个
Student
构造函数,继承自Person
。Student
应该有一个额外的major
属性。在Student.prototype
上添加一个study
方法,打印出[name] is studying [major]
。
4.3 复杂练习¶
- 创建一个
Professor
构造函数,继承自Person
。Professor
应该有一个额外的department
属性和一个teach
方法,打印出[name] is teaching in the [department] department
。然后创建一个Course
构造函数,包含name
和professor
属性。在Course.prototype
上添加一个describe
方法,打印出This course is [name], taught by [professor.name]
。
5. 总结¶
- 原型链:JavaScript 中的对象通过原型链继承属性和方法。每个对象都有一个
[[Prototype]]
属性,指向它的原型对象。 - 构造函数:通过构造函数可以创建对象,并将对象的原型设置为构造函数的
prototype
属性。 - 原型继承:通过将一个构造函数的原型设置为另一个构造函数的实例,可以实现继承。
- 继承的实现:在 JavaScript 中,继承通常通过原型链来实现,可以通过
Object.create()
和call()
方法来实现继承。
理解原型和继承是掌握 JavaScript 面向对象编程的关键。通过原型链,JavaScript 提供了一种灵活且强大的机制来实现对象的继承和共享行为。