首先来理清楚原型链里面的prototype和__proto__这两个要素,下面来一个例子
<script>
Person.prototype.name = "xiaomi";
function Person() {
}
Toni.prototype.name = "heheda";
function Toni() {
}
let person1 = new Person();
console.log(person1.name); // xiaomi
person1.name = "NewName";
console.log(person1.name); // NewName
</script>
上面的例子中,创建了一个构造函数Person,person1调用Person的构造函数创建了一个新的实例。那么person1的内部属性指针( __proto__ )就是指向构造函数Person的原型对象(prototype);当执行person1 = new Person的时候,实际上在Person函数里面创建了一个this对象,里面包含一个__proto__属性,值就是Person.prototype。如下:
function Person () {
this = {
__proto__ : Person.prototype
}
}
当访问对象属性的时候,如果没有找到相应的属性,就会根据__proto__属性指向的索引去继续查找。拿上面的例子来说就是,当调用person1的属性name时,首先会在person1对象本身中进行搜索,如果找到了,就返回该属性相应的值;如果没有找到,则根据person1的内部指针属性,往上一级进行搜索,如果还是没找到,还是会继续往上一级进行搜索,一直到该实例对象的最顶端的原型对象,到最后都是没有找到就会返回null。这一个过程,我们称之为原型链。
__proto__的指向是可以修改的,现在将person1的__proto__指向到Toni看看会怎么样
Person.prototype.name = "xiaomi";
function Person() {
}
Toni.prototype.name = "heheda";
function Toni() {
}
var toni2 = new Toni;
var person1 = new Person;
person1.__proto__ = toni2; // 修改__proto__的指向
console.log(person1.name); // heheda
person1.name = "NewName";
console.log(person1.name); // NewName
这时候输出person1.name就是heheda。
再来看另外一个例子:
Person.prototype.name = "xiaofeng";
function Person () {
this.car = "BMW";
}
var a = new Person;
Person1.prototype = a;
function Person1 () {
this.car = "Audi";
}
var b = new Person1;
Person2.prototype = b;
function Person2 () {
}
var c = new Person2;
console.log(c);
console.log(c.name);
console.log(c.name)输出的是 xiaofeng,原因是Person2继承了Person1的原型属性和方法,而Person1也继承了Person的属性和方法,这一个过程就可以理解为js的继承。
js的继承是基于原型链来实现的,利用原型让一个引用继承另一个引用类型的属性和方法。
构造函数、原型、实例这三者的关系是:
- 每个(构造)函数都有一个prototype属性,它是一个对象;
- 每个原型对象都有一个constructor属性,这个属性指向原型对象所在的(构造)函数;
- 每个构造函数的实例都包含一个指向原型对象的属性 __proto__ ;(实际上,除了最顶层的Object对象以外,每一个对象都拥有__proto__属性)
该文章例子为本人理解js原型链和js继承所写,如描述不到位还望指正。