javascript 高级程序设计 8.3.1 原型链

190 阅读4分钟

构造函数,原型,实例之间的关系 

1. 每个构造函数都有一个原型对象 

2. 原型有一个属性指回构造函数 

3. 实例有一个内部指针指回原型 

上面这三句话我认为听起来还是比较不舒服的.抽象的.

我个人是这么看待的

1.如果我定义了一个构造函数,那么我就可以去 函数名.prototype 来取到函数的原型.

2.原型对象里,默认肯定会有一个constructor属性,这个属性就是这个原型的构造函数

3.通过构造函数new出来的实例,可以通过 实例.__proto__ 去取到这个实例的原型

原型链

实例有自己的原型,而原型也可能会作为实例,有自己的内部指针指向它自己的原型,这样就可以层层继承,达成原型链.

举个例子

// 我们定义一个构造函数张三,张三有个特点,出生就会降龙十八掌
function zhangsan() {
    this.zhangsankongfu='直接使用降龙十八掌'
}
// 它虽然会,但是平时不使用,只有在危险的时候才会使用
zhangsan.prototype.nowIsDanger = function() {
    console.log(this.zhangsankongfu)
}
// 我们再定义一个李四,也是构造函数,李四也有个特点,出生会凌波微步,但是它不会用
function lisi() {
    this.lisikongfu = '直接使用凌波微步'
}
// 某一天,李四遇到张三,张三见李四骨骼惊奇,就把自己一身本领传授李四了
lisi.prototype = new zhangsan()
// 李四在遇到需要逃跑的情况的时候,可以使用凌波微步逃跑
lisi.prototype.nowIsRun = function() {
    console.log(this.lisikongfu)
}
// 这样李四作为张三的传人,它new出来的实例,就非常了不起,两个功夫都会
let person = new lisi()
person.nowIsDanger() // 直接使用降龙十八掌
person.nowIsRun() // 直接使用凌波微步

// 这里需要注意的点是: lisi.prototype = new zhangsan() 这一步是灵魂所在
// 这一步把lisi的原型给替换了,李四的默认原型不复存在,取而代之的是张三的原型

默认原型

实际上,任何函数的默认原型都是object,所有的实例追根溯源最后都能通过内部指针找到object原型,而object再往上找,就是null了.

原型与继承关系

原型和实例之间的关系,可以通过两个方法确定:instanceof 和 isPrototypeOf()

instanceof : 实例 instanceof 构造函数 

如果实例的原型链里出现了这个构造函数,就返回true

例如: console.log(instance instanceof Object); // true

isPrototypeOf(): 构造函数.原型.isPrototypeOf(实例)

原型链里的原型可以调用这个方法,只要实例的原型链里有这个原型,就返回true

例如: console.log(Object.prototype.isPrototypeOf(instance)); // true

原型的方法

先原型赋值,再修改属性方法

我们知道原型链里最关键的一步,就是把一个构造函数的原型改成另一个构造函数的实例来继承其属性和方法,就像上面的李四继承张三的功夫一样.但是有的时候可能会需要覆盖继承来的属性和方法,那就需要在用新的实例覆盖旧的原型之后,再去进行修改,因为如果先修改后覆盖,自然原来的自己修改的就都没了.

不要用字面量去修改原型

如果我们给原型赋值为一个字面量的对象,就算对象里的属性和方法和原本继承来的属性方法都一样,也不可以,因为他们之间的原型链就断了.新生成的字面量对象的原型是默认的object.

原型链的问题

引用类型数据的共享

在构造函数A里定义引用数据类型arr,然后通过这个构造函数new出来的实例,每一个里面的引用数据类型都是属于自己的。但是如果这个构造函数A的实例是另一个构造函数B的原型呢?A里面的引用数据类型就会跑到构造函数B的原型上,而原型上的引用数据类型是会共享的。这会导致一个现象就是:构造函数 B new 出来的实例,里面的arr属性,是共享的,比如B有两个实例: B1和B2 ,他们两个的arr,是一样的,B1修改了arr,B2的arr也会跟着变。这个有时候是我们不想要的。

子类型实例化的时候没办法给父类型的构造函数传参.

如果想给父类构造函数传参,就得添加构造函数的入参,这样所有调用这个构造函数的实例,都得跟着受到影响

因为没办法向构造函数传参,并且原型里的引用类型有可能会被乱分享,所以原型链基本上不会被单独使用.