1、继承的三种方式:
原型链继承、经典继承、组合继承
2、js如何实现继承?
继承是面向对象编程中讨论最多的话题。很多面向对象语言都支持两种继承:接口继承和实现继承。前者只继承方法签名,后者继承实际的方法。接口继承在 ECMAScript 中是不可能的,因为函数没有签名。实现继承是 ECMAScript 唯一支持的继承方式,而这主要是通过原型链实现的。
3、总结
原型链继承:Dog.prototype=new Animal()
经典继承:Animal.call(this)
组合继承:方法Dog.prototype=new Animal() 、属性Animal.call(this)
一、原型链
1、原型链继承
定义:由多个原型对象(xx.prototype)连起来的原型关系链
语法:Dog继承Animal()
Dog.prototype = new Animal()
继承原理:
- 每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型。 (图解如下:)
将父的构造函数赋值给子原型对象,子原型对象就会成为父构造函数的实例,此时就会产生_proto_指针,指向父原型对象,这样子原型对象就继承了父原型对象中的方法(父构造函数上面还有一个Object)。图解如下:
例子:
检验原型链:
结论:由此可知,子实例继承了Dog、Animal、Object原型对象中的方法和实例属性
2、原型链的方法使用
1、在子原型对象中新增方法
例子:
注意:一定要先继承再对子原型对象新增方法
产生的问题:子实例构造函数类型模糊
解决办法:改变子原型对象constructor指向
引发问题:
改变constructor指向是否会破坏原型链?
答:不会原理链的继承是通过_proto_完成,而constructor只是说明它是哪个构造函数的实例
父构造函数的实例是否能使用子构造函数的方法?
答:不可以
2、在子原型对象中重写父原型对象的方法
例子:
父实例是否会受到影响?不会()
3、如何破环原型链?
答:使用字面量定义子原型对象的方法
原因:当使用字面量定义方法时,_proto_就会指向Object而不是Animal(父原型对象),原来定义的Dog.prototype=new Animal()就会作废,原型链就此断开
例子:
4、原型链的问题
1、问题
当子构造函数的实例修改父构造函数的属性值,所有的子类实例都会受到影响(原因:子实例共享的是同一个构造函数 类似于 我们原型模式遇到的问题)
子实例化时,不能给父类型的构造函数传参
2、例子:
- 通过原型图可知,d1、d2共享一个Dog.prototype,所以就变成原型模式遇到的问题。而我们修改的是Dog.prototype,因此父不会受到影响
3、d1添加,d2为什么会受到影响?
因为this存在于Animal中,而Animal被Dog.prototype调用,所以this指向了Dog.prototype,因此categeorys[ ]由原来的实例属性转变为原型属性存在于Dog.prototype中
4、解决办法:使用经典继承
经典继承
优点:可以在子类构造函数中向父类构造函数传参
缺点:
1、与构造函数遇到的问题一样,方法需要占很多内存
2、没有原型链,因此只继承了父类的实例属性、方法,不能继承父类构造函数的原型对象中的属性、方法
3、每个子类的实例都持有父类的实例方法的副本,浪费内存,影响性能,而且无法实现父类的实例方法的复用(不能重写)
例子:
组合继承
继承方法:原型链;
继承实例属性:经典继承
例子: