# JS面对对象基础--原型链
它没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instance)的区分,全靠一种很奇特的"原型链"(prototype chain)模式,来实现继承。
我们先来回顾一下构造函数和原型模式组合创建实例对象:
function Animal(name) {
this.name = name;
}
//必须是使用prototype.属性来添加原型;
//不能对prototype={}来对它重新赋值!!!
Animal.prototype.species='动物'
let dog = new Animal('狗');
let cat = new Animal('猫');
console.log(dog, cat)//{ name: '狗' } { name: '猫' }
console.log(dog.species, cat.species)//动物 动物
//如果new出来的实例对象本身上没有的属性,就会去创建时原型对象上面找该属性。
//所以这里访问到的是Animal上的原型上属性
dog.species = '犬科';
cat.species = '猫科';
//这里就看做是:只是简单的向对象中添加一个属性
console.log(dog.species, cat.species)//犬科 猫科
//此时new出来的实例对象本身上有了新赋的属性,就直接访问找了该属性,就不需要到原型对象上面找
console.log(Animal.prototype.species)//动物
//原型上的属性只能通过一下方式修改:
//1.实例访问原型对象修改
dog.__proto__.species = '修了改原型属性'
console.log(Animal.prototype.species)//修了改原型属性
//2.构造函数访问原型对象修改
Animal.prototype.species = '又修了改原型属性'
console.log(Animal.prototype.species)//又修了改原型属性
//3.构造函数中默认的prototype属性和原型对象中默认的constructor属性相互指向
dog.__proto__.constructor.prototype.species = '又又修了改原型属性'
console.log(Animal.prototype.species)//又又修了改原型对象属性
关于__proto__
隐式原型属性访问原型:
本来在实例对象访问自己原型对象的属性的时候,是需要带上__proto__
的,在某写情况下可以省略;有些情况不能省略;
- 当原型链由下向上找属性的过程中,如果不同原型对象上具有相同的属性名的属性,此时如果我们需要用原型对象处于原型链更上层的那个属性,我们就不能省略
__proto__
,因为在原型链由下至上找的过程中,找的第一个对应的属性名就停止了。 - 实例对象访问一个属性的全过程(原型链):
- 1.先找自己本身对象空间中的属性,找到了停止;
- 2.如果没找到,使用
__proto__
找上一个原型对象,找到了停止; - 3.如果没有找到继续,继续沿
__proto__
,由下至上地找,直到找到为止; - 4.如果都没有对应的属性名,则返回
undefined
- 注意:此过程
__proto__
可以省略。
由于所有的实例对象共享同一个prototype
对象,那么从外界看起来,prototype
对象就好像是实例对象的原型,而实例对象则好像"继承"了prototype
对象一样。所以JavaScript是通过原型链的机制来实现继承。