JavaScript 中的原型链(Prototype Chain)和继承机制是 JavaScript 对象模型的核心概念之一,它们共同构成了 JavaScript 面向对象编程的基础。下面将详细介绍这两个概念。
原型链(Prototype Chain)
在 JavaScript 中,每个对象都有一个内部属性 [[Prototype]]
(也称为 __proto__
,尽管 __proto__
是一个非标准但广泛实现的属性,并且不建议在生产环境中使用),这个属性会指向另一个对象。如果尝试访问一个对象的属性或方法时,它本身没有这个属性或方法,那么就会去它的 [[Prototype]]
指向的对象中查找,这个过程会一直持续下去,直到找到该属性或方法或到达原型链的顶端(即 null
)。这个过程就是原型链的查找机制。
原型链的顶端是 Object.prototype
,它的 [[Prototype]]
属性值为 null
。这意味着所有普通的 JavaScript 对象最终都会继承自 Object.prototype
。
继承机制
JavaScript 的继承机制主要基于原型链。当一个构造函数(或类,在 ES6 及以后版本中)创建一个新对象时,这个新对象的内部 [[Prototype]]
属性会被设置为构造函数的 prototype
属性所指向的对象。这样,通过构造函数创建的所有对象都会共享同一个原型对象,并因此继承原型对象上的属性和方法。
示例
function Parent(name) { | |
this.name = name; | |
} | |
Parent.prototype.sayHello = function() { | |
console.log('Hello, my name is ' + this.name); | |
}; | |
function Child(name, age) { | |
Parent.call(this, name); // 借用构造函数继承属性 | |
this.age = age; | |
} | |
// 设置 Child 的原型对象为 Parent 的实例 | |
Child.prototype = Object.create(Parent.prototype); | |
Child.prototype.constructor = Child; // 修复 constructor 指向 | |
Child.prototype.sayAge = function() { | |
console.log('I am ' + this.age + ' years old.'); | |
}; | |
var child = new Child('Alice', 10); | |
child.sayHello(); // 继承自 Parent.prototype | |
child.sayAge(); // Child.prototype 上的方法 |
在上面的示例中,Child
构造函数通过 Object.create(Parent.prototype)
创建了一个新的原型对象,这个新对象作为 Child.prototype
,并且它的 [[Prototype]]
指向 Parent.prototype
。这样,Child
的实例 child
就能够访问到 Parent.prototype
和 Object.prototype
上的方法,实现了继承。
原型链的补充说明
- 原型链的查找:当访问一个对象的属性时,JavaScript 引擎会首先查找该对象自身是否有该属性,如果没有,则继续查找其原型对象,依此类推,直到找到该属性或到达原型链的顶端(
null
)。 - 方法重写:如果子类(或子对象)的原型上有一个与父类(或父对象的原型)同名的属性或方法,那么子类(或子对象)的实例在访问该属性或方法时会优先使用子类(或子对象)原型上的版本,即实现了方法重写。
- 原型链的扩展:通过为对象的原型添加新的属性或方法,可以扩展该对象及其所有实例的功能。但请注意,这样做会修改所有现有实例和新创建的实例的行为。
JavaScript 的这种基于原型的继承机制与传统的基于类的继承机制(如 Java、C++)不同,它提供了更灵活和动态的对象继承方式。