面向对象语言有两种继承方式:接口继承(只继承方法名);实现继承(继承实际的方法)。但在ECMAScript中,函数名没多大含义,只是函数体的引用而已,因此,ECMAScript无法实现接口继承,只支持实现继承。实现继承,主要是依靠原型链来完成的。
2.构造函数、原型、实例之间的关系
(1)每个构造函数都有一个原型对象,Func.prototype指向了这个原型对象。
(2)原型对象都包含一个指向构造函数的指针,Func.prototype.constructor等于Func,可以理解为prototype和constructor是两个方向相反的指针,在构造函数和原型之间搭建起桥梁。
(3)每个实例,都包含一个指向原型对象的内部指针__proto__,当然这个对开发人员是不可见的。
(4)如果把构造函数A的原型,设成构造函数B的一个实例,那么构造函数A的原型里就包含了指向另一个原型的指针,从而形成了原型链。这样就实现了继承。使用对象的属性或方法时,解析器先搜索实例内部,找不到就搜索实例的原型内部,再找不到就搜索实例的原型的构造函数的原型内部,一级一级向上查找。
(5)原型链的最顶端是Object,这就是为什么所有引用类型instanceof Object都会返回true。换句话说,只要instanceof后面的构造函数在实例的原型链中,都会返回true。
(6)注意,由于继承的时候,原型被重写了,所以tom的constructor指像的不再是Male,而是Male.prototype的构造函数Human。
3.使用原型链做继承的问题。
(1)和原型模式创建对象一样,使用原型链做继承,也会遇到多个实例共享object(引用类型)属性的情况。这也就是为啥引用类型属性要放在构造函数内部。
(2)更严重一个问题是,使用原型链做继承时,必须用基类的实例去替换派生类的原型,产生基类的实例的过程是不能使用参数的,这就大大降低了继承的灵活性。
2.当然,构造时不传递参数用处还是不大,幸好apply和call都是支持传参的。
3.借用构造函数解决了引用类型属性的问题,但没有解决方法的问题。
2.这是目前最常用,也是解决最完美的继承写法了,没有之一。
一、原型链
1.原型链的基本思想是:利用原型让一个引用类型继承另一个引用类型的属性和方法。2.构造函数、原型、实例之间的关系
(1)每个构造函数都有一个原型对象,Func.prototype指向了这个原型对象。
(2)原型对象都包含一个指向构造函数的指针,Func.prototype.constructor等于Func,可以理解为prototype和constructor是两个方向相反的指针,在构造函数和原型之间搭建起桥梁。
(3)每个实例,都包含一个指向原型对象的内部指针__proto__,当然这个对开发人员是不可见的。
(4)如果把构造函数A的原型,设成构造函数B的一个实例,那么构造函数A的原型里就包含了指向另一个原型的指针,从而形成了原型链。这样就实现了继承。使用对象的属性或方法时,解析器先搜索实例内部,找不到就搜索实例的原型内部,再找不到就搜索实例的原型的构造函数的原型内部,一级一级向上查找。
(5)原型链的最顶端是Object,这就是为什么所有引用类型instanceof Object都会返回true。换句话说,只要instanceof后面的构造函数在实例的原型链中,都会返回true。
(6)注意,由于继承的时候,原型被重写了,所以tom的constructor指像的不再是Male,而是Male.prototype的构造函数Human。
3.使用原型链做继承的问题。
(1)和原型模式创建对象一样,使用原型链做继承,也会遇到多个实例共享object(引用类型)属性的情况。这也就是为啥引用类型属性要放在构造函数内部。
(2)更严重一个问题是,使用原型链做继承时,必须用基类的实例去替换派生类的原型,产生基类的实例的过程是不能使用参数的,这就大大降低了继承的灵活性。
二、借用构造函数
1.为了解决原型链做继承遇到的第一个问题,出现了借用构造函数的方法。思想是把基类的构造函数当作普通函数执行一次,但执行的时候,必须把派生类的执行空间传过去。2.当然,构造时不传递参数用处还是不大,幸好apply和call都是支持传参的。
3.借用构造函数解决了引用类型属性的问题,但没有解决方法的问题。
三、组合继承(伪经典继承)
1.这种继承是把原型链和借用构造函数结合起来。用借用构造函数处理属性,用原型链处理方法。其实就相当于创建对象时提到的“组合模式”2.这是目前最常用,也是解决最完美的继承写法了,没有之一。