JavaScript继承
0、原型和原型链
每个构造函数都会有一个prototype属性,指向它的原型对象,原型对象有一个constructor属性,指向构造函数,实例对象会有一个内部属性proto指向构造函数的原型对象,原型链就是通过proto一形成的一个链接,链接的顶部是Object.prototype,Object.prototype的proto属性指向null1
2
3
4
5
6
7
8
9
10function Animal(name) {
this.name = name
}
Animal.prototype.getName = function() {
return this.name
}
Animal.prototype.constructor === Animal // true
const cat = new Animal('miao')
cat.__proto__ === Animal.prototype // true
Object.prototype.__proto__ === null // true
1、原型链继承
我们知道原型链是通过对象的proto属性形成的一条链,原型链继承自然是通过proto继承
1 | function Animal(name) { |
cat和myCat都是Cat构造函数的实例,我们通过给Cat.prototype重新赋值Animal的实例animal,使得cat和myCat的proto都指向animal,而animal具有name属性,animal.proto指向Animal.prototype,所以最终才可以使用getName获取name。
总结:让子类实例的原型(proto)指向父类的一个实例
优点:可以继承父类原型对象上的属性和方法
缺点:1、父类的引用属性会被所有子类实例共享
2、实例化子类时不能向父类传参
2、构造函数继承
构造函数继承就是在子类构造函数中执行父类构造函数,并绑定this为子类实例(将父类实例的属性复制到在子类上),上代码1
2
3
4
5
6
7
8
9
10function Animal(name) {
this.name = name
this.footer = 'footer'
this.getName = function() {}
}
function Cat(name, age) {
this.age = age
Animal.call(this, name)
}
const cat = new Cat('miao', 3)
如上代码所示,构造函数继承的重点是在子类构造函数中执行父类构造函数Animal.call(this, name),将父类实例该有的属性都复制过来
总结:重点在于Animal.call(this, name)
优点:
缺点:1、只能继承父类实例上该有的属性,无法继承父类实例原型上的属性和方法
2、无法复用共有属性和方法,上述例子中,通过实例化Cat创建3个实例,三个实例中都有getName方法,造成内存浪费
3、组合继承
组合继承从名字就可以看出来他不是新鲜玩意,他是将上述第一种继承(原型链继承)和第二种继承(构造函数继承)组合起来的一种方法,它既可以继承到父类构造函数原型上的共有属性和方法,也可以继承到父类实例自身该有的属性和方法,不多说上代码
1 | function Animal(name) { |
通过上述代码发现,实例cat和其原型cat.proto(animal)上都有父类(Animal)实例该有的属性,只不过cat上的属性会屏蔽其原型上的同名属性和方法
总结:将原型链继承和构造函数继承结合起来
优点:可以将父类原型对象上的属性、方法和父类实例自身该有的属性、方法都继承过来
缺点:子类实例和子类实例原型上存在一部分同名属性和方法,造成内存浪费
4、原型式继承
原型式继承和Obje.create()方法原理一致,但是又有点区别
1 | function extend(obj) { |
原型式继承和原型链继承差不多,都是通过修改构造函数的prototype为指定对象,但是原型式继承将这种操作包在了函数里边,另外原型式继承实际上是将参数对象person进行了一次浅复制
总结
优点:可以复用父类方法
缺点:父对象的引用属性会被所有子实例共享;创建子实例时无法传参
上述缺点已被修正,就是Object.create方法
5、寄生式继承
寄生式继承是在原型式继承的基础上进行了增强,怎么增强?上代码
1 | function extend(obj) { |
可以看出寄生式继承是在函数内通过原型式继承创建一个对象,然后进行增强,最后返回
总结:在原型式继承的基础上进行了增强
优点:可以复用父类方法,并且可以进行增强
缺点:父对象的引用属性会被所有子实例共享;创建子实例时无法传参(和原型式继承一样)
6、寄生组合式继承
在第3点介绍的组合继是是原型链继承和构造函数继承的组合,它的缺点有一条是父构造函数调用了两次,造成了浪费,我认为寄生组合式继承是寄生式继承、原型链继承和构造函数继承的组合,父构造函数只会调用一次
1 | function inherit(Sub, Sup) { |
优点:
缺点:无
7、ES6 Class extends继承
JavaScript是基于原型继承的语言,即使ES6新增了class,他依然是基于原型继承的,class只是一种语法糖,es6中的类继承的关键字式extends,并且在子类中通过调用super方法将父类实例对象上的方法和属性加到子类实例上1
2
3
4
5
6
7
8
9
10
11class Animal {
constructor(name) {
this.name = name
}
}
class Cat extends Animal {
constructor(name) {
super(name)
}
}
typeof Cat // function
8、参考文章