在JavaScript中,每个函数都有一个prototype属性,它指向一个对象,这个对象包含可以被该函数实例共享的属性和方法。
当创建一个对象时,该对象会从它的构造函数那里继承prototype对象。
当尝试访问一个对象的某个属性或者方法时,如果该对象本身没有这个属性或方法,JavaScript引擎会沿着原型链向上查找,直到找到这个属性或方法为止。
如果在整个原型链上都没有找到,则认为该属性或方法不存在。
1.1. 原型链工作原理的一个简要概述:
对象与原型:
- 每个对象都有一个内部链接[[Prototype]],指向它的原型对象。
- 普通对象的原型通常是Object.prototype。
- 函数对象也有一个prototype属性,用于作为其构造函数创建的对象的原型。
构造函数与原型:
- 构造函数创建的对象会继承该构造函数的prototype对象。
- 构造函数的prototype对象通常包含一些共享的方法和属性。
原型链的查找:
- 当访问一个对象的属性或方法时,如果该对象自身没有这个属性或方法,则JavaScript引擎会查找该对象的[[Prototype]]。
- 如果在[[Prototype]]中也没有找到,则继续沿着[[Prototype]]链向上查找。
- 查找过程一直持续到Object.prototype,这是原型链的终点,其[[Prototype]]值为null。
1.2. 一个简单的示例来说明原型链的运作方式:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log('Hello, ' + this.name);
};
function Student(name, grade) {
Person.call(this, name); // 调用父类构造函数
this.grade = grade;
}
// 设置Student的原型为Person的实例
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student; // 修复constructor指向
Student.prototype.study = function(subject) {
console.log(this.name + ' is studying ' + subject);
};
const student1 = new Student('Alice', 10);
console.log(student1.name); // "Alice"
student1.sayHello(); // "Hello, Alice"
student1.study('Math'); // "Alice is studying Math"
在这个例子中:
- Person构造函数定义了一个sayHello方法。
- Student构造函数继承自Person。
- Student还添加了自己的study方法。
- 创建了一个Student实例student1。
当我们访问student1的name属性时,它直接存在于student1对象中。
但是当我们调用sayHello方法时,这个方法不在student1对象中,因此会沿着原型链查找,并最终在Person.prototype中找到。
这就是原型链的基本工作原理。
通过这种方式,JavaScript实现了基于原型的继承机制。