文章目录

一、什么是原型链?
在 JavaScript 中,每个对象都有一个内部属性 [[Prototype]],指向另一个对象,这个指向的对象被称为原型(prototype)。通过原型,子对象可以访问父对象的属性和方法。原型链就是一系列对象通过 prototype 属性连接起来的链条,最终指向 null。
二、原型链的基本原理
- 每个 JavaScript 对象都有一个
prototype属性,指向其原型对象。 - 当访问一个对象的属性时,JavaScript 引擎会先查找对象本身,如果找不到,则会顺着
[[Prototype]]链向上查找,直到找到该属性或者到达链条的末端null。 - 如果原型链中的每个对象都没有找到该属性或方法,最终返回
undefined。
三、原型链的构建
- 对象的原型:
- 对于由构造函数创建的对象(如
new Object()、new Array()等),它们的原型对象是该构造函数的prototype属性。 Object的原型是null,即原型链的终点。
- 对于由构造函数创建的对象(如
- 构造函数的
prototype属性:- 每个构造函数都有一个
prototype属性,这个属性是一个对象,包含了该构造函数创建的实例的默认属性和方法(如constructor属性)。
- 每个构造函数都有一个
四、原型链的例子
1. 基本示例:构造函数与原型链
function Animal(name) {
this.name = name;
}
Animal.prototype.sayHello = function() {
console.log(`${this.name} says hello!`);
};
const dog = new Animal('Buddy');
dog.sayHello(); // 输出 "Buddy says hello!"
console.log(dog.__proto__ === Animal.prototype); // true
dog是Animal构造函数的一个实例。dog对象没有sayHello方法,但是它的原型dog.__proto__指向Animal.prototype,所以dog可以访问Animal.prototype中的方法。dog.__proto__ === Animal.prototype返回true,证明dog的原型链确实指向了Animal.prototype。
2. 多级原型链
function Animal(name) {
this.name = name;
}
Animal.prototype.sayHello = function() {
console.log(`${this.name} says hello!`);
};
function Dog(name, breed) {
Animal.call(this, name); // 继承 Animal 的属性
this.breed = breed;
}
// 继承 Animal 的方法
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.sayHello(); // 输出 "Buddy says hello!"
console.log(myDog instanceof Dog); // true
console.log(myDog instanceof Animal); // true
Dog通过Object.create(Animal.prototype)设置了自己的原型链指向Animal.prototype,从而继承了Animal的方法。myDog可以访问Animal.prototype中的方法,并且myDog instanceof Dog和myDog instanceof Animal都返回true,说明myDog既是Dog的实例,也是Animal的实例。
3. 原型链的继承与 constructor
function Animal(name) {
this.name = name;
}
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // 修复 constructor 指向
const myDog = new Dog('Buddy', 'Golden Retriever');
console.log(myDog.constructor); // Dog() 构造函数
- 在上面的代码中,
Dog.prototype.constructor被设置为Dog,以修复继承链中的constructor指向,使得myDog.constructor返回的是Dog构造函数。
五、原型链的特点
- 共享属性和方法:
- 原型链的最大特点是,多个实例可以共享原型对象上的方法和属性,而不需要每次创建实例时都重新定义。
- 性能问题:
- 访问原型链上的属性时,性能相对较低,因为 JavaScript 需要逐层查找原型链,直到找到属性或者原型链的末尾。
- 如果有大量的原型链查找,可能会影响性能,因此应尽量避免在原型链上放置频繁访问的属性。
- 原型链的终点是
null:- 所有对象的原型链最终都会指向
null,这标志着原型链的结束。
- 所有对象的原型链最终都会指向
六、__proto__ 和 prototype 的区别
prototype是构造函数上的属性,用来为该构造函数创建的对象实例提供原型对象。__proto__是对象上的属性,指向该对象的原型对象,即该对象的构造函数的prototype属性。
示例:
function Animal(name) {
this.name = name;
}
const dog = new Animal('Buddy');
console.log(dog.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
七、总结
- 原型链 是对象和构造函数之间的一个连接机制,它使得对象能够继承构造函数的属性和方法。
- 每个对象都通过
[[Prototype]]指向其构造函数的prototype属性,而构造函数的prototype属性指向其父类的原型对象,最终形成一个链条,链条的尽头是null。 - 通过原型链,JavaScript 实现了继承的机制,允许对象共享属性和方法。