一、原型链简介
当谈到 JavaScript 中的原型链时,我们需要先理解几个关键概念:构造函数、原型、原型对象和原型链。
1. 构造函数:在 JavaScript 中,构造函数是用于创建对象的函数。当使用 `new` 关键字调用构造函数时,会创建一个新对象,并将该对象的原型指向构造函数的原型对象。
2. 原型对象:每个 JavaScript 对象都有一个原型对象,它包含该对象的方法和属性。原型对象可以被共享,即多个对象可以共享同一个原型对象。
3. 原型链:原型链是一种机制,用于实现对象之间的继承关系。当访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到对应的属性或方法或者到达原型链的顶端(即 `Object.prototype`)。 通过原型链,JavaScript 实现了基于原型的继承机制,这使得对象之间可以共享属性和方法,同时也保留了各自的特定属性和方法。
下图是对原型链的简单示意:
原型链:
二、原型对象
如上图所示,在js中,每一个实例对象都有一个__proto__属性指向自己的原型对象,而这个原型对象也会有一个__proto__属性指向自己的原型对象,这样一直往后重复,直到Object的原型对象为null停止,这就是原型链。我们可以用构造函数的方式来创建一个对象来验证,我们用Obj构造函数构造Obj对象,然后往上查看这个对象的__proto__属性。
//构造函数
let Obj = function(name) {
this.name = name
}
//利用构造函数创建对象
let obj = new Obj('test')
//获取对象的原型对象
let a = obj.__proto__
//再获取该对象的原型对象 b的雷响为Object
let b = a.__proto__
//再获取原型对象,c为null原型链终止
let c = b.__proto__
在上述代码中,对象obj的原型对象的原型对象为Object,而Object的原型对象为null。如下图所示。
三、构造函数
讲完原型对象,就不得不说说构造函数。构造函数对象有一个prototype属性,其指向的对象和该构造函数构造的实例对象的__proto__属性指向的是同一个对象(原型对象)。接着上面的例子,我们比较一下Obj(构造函数)的prototype和obj(实例对象)的__proto__属性,最后发现这两个属性指向的是同一个对象。
obj.__proto__ === Obj.prototype //true
在描述原型链的图中,也提到过,对象的原型对象的constructor属性指向对象的构造函数,我们也可以用代码验证
obj.__proto__.constructor === Obj //true
至此整个原型链的描述结束,为了更加深入的理解js的原型链和构造函数的关系,这里我们可以自己简单实现一下new关键字。
//第一个参数为构造函数,可变参数为构造函数的参数
let myNew = function(Constructor, ...args) {
//用Object的create方法来构造Constructor指向的原型对象
let tmp = Object.create(Constructor.prototype);
//创建一个对象,将其this指针指向tmp(即作为该对象的原型对象)
let res = Constructor.call(tmp, ...args);
//判断res对象是否是object是就直接返回,否则返回tmp
if (typeof res === 'object') return res;
else return tmp;
}
接着我们用myNew来创建Obj对象,并验证与new创建的Obj对象是否是同一个类。
let obj2 = myNew(Obj, 'obj2')
obj2.__proto__ === obj.__proto__ //true
obj.__proto__ === Obj.prototype //true
结果正确。
四、原型链的作用
原型链的一大作用是通过原型对象为对象增加方法,例如我们可以通过原型对象来为obj添加talk方法。
obj.__proto__.talk = function() {
console.log(this.name);
}
obj.talk() //输出test
obj2.talk() //输出obj2
还有一大作用即通过原型链来实现继承,例如
//父类构造函数
function Animal(name) {
this.name = name;
}
//为父类增加方法
Animal.prototype.sayName = function() {
console.log("My name is " + this.name);
};
//子类构造函数
function Dog(name, breed) {
Animal.call(this, name); //将当前上下文this绑定到Animal的构造函数上
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log("Woof! Woof!");
};
let myDog = new Dog("Buddy", "Golden Retriever");
myDog.sayName(); // 输出:My name is Buddy
myDog.bark(); // 输出:Woof! Woof!
五、总结
原型链是 JavaScript 中的一个重要特性,它是实现对象之间继承关系的机制。在 JavaScript 中,每个对象都有一个原型(prototype)属性,指向另一个对象。当试图访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到对应的属性或方法为止。