继承
子类去继承父类的方法和属性,在这基础上进行拓展,称为父类的超集。本质上就是一种代码的复用形式,节约代码。
原型继承
将父实例赋值到子类的原型上。
function Person(name){
this.name = name
if (typeof this.run != 'function') {
Person.prototype.run = function(){
return 'run'
}
}
}
function Child(name){}
Child.prototype = new Person()
let obj = new Child('son')
缺点:
- 由于“共享”性,子类修改,父类也会被修改。
- 子类实例化,无法向父类构造函数传递参数,例如例子中传入的son,父类中this并无指向父类和子类。
- 每次继承都要new一次父类,造成副本过多。
改变this指向继承
通过例如call()方法,将子类中的this指向父类:
function Person(name){
this.name = 'dad'
if (typeof this.run != 'function') {
Person.prototype.run = function(){
return 'run'
}
}
}
function Child(){
Person.call(this)
}
let obj = new Child('son')
优点:子类实例可向父类传参。
缺点:
- 每new出的一个对象都是一个独立个体的对象,所以每个对象的方法并不是等同(==)的,造成内存浪费资源(构造函数固有缺点)。
- 无法继承父类后续添加的原型链上的方法。
原型+改变this指向组合继承
function Person(name){
this.name = 'dad'
if (typeof this.run != 'function') {
Person.prototype.run = function(){
return 'run'
}
}
}
function Child(name){
Person.call(this, name) // 这里继承独立属性
}
Child.prototype = new Person() // 这里继承共享方法
let obj = new Child('son')
优点:
- 子类可继承到独立属性又可继承共享属性,方法。
- 子类实例可向父类传参。
缺点:因为用同时用两种方式继承,多继承一份父类实例属性。
根据缺点,可以把原型继承哪里改写一下:
Child.prototype = Parent.prototype // 这样的好处是不用再多继承一次构造函数里的东西
原型工厂继承
看例子:
function Person(name){
this.name = 'dad'
if (typeof this.run != 'function') {
Person.prototype.run = function(){
return 'run'
}
}
}
// 这个函数把父类丢进去后,返回一个原型附有父类的构造函数对象
function createObject(obj) {
function Fn(){}
Fn.prototype = obj
return new Fn()
}
let child = createObject(new Person())
优点:从已存在对象繁衍出新对象,不需要new。
缺点:子类属性只能后续单独添加,无法实现代码复用。之前子类的属性都是封装成一个函数的,现在没有了,要你继承父类之后手动挨个添加。
继承方案还有例如寄生式,寄生组合式等,个人觉得es5的话就只需要记住原型+改变this指向组合继承的方法就可以了,没必要记太多。