前言
本篇的学习建立在对原型链的理解之上,如果对原型链还不够了解,可以先自行学习相关知识
1.原型链继承
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | function Animal() {this.name = 'animal'
 }
 
 function Dog() {
 this.eat = 'eat shit'
 }
 
 Animal.prototype.getName = function() {
 return this.name
 }
 
 Dog.prototype = new Animal()
 
 const dog = new Dog()
 
 console.log(dog.getName())
 console.log(dog.eat)
 
 | 
原型链继承的本质就是将需要继承的构造函数(Dog)的原型prototype指向被继承的构造函数(Animal)的实例,这样就获得Animal上的所有属性与方法了。
缺点:
- 原型中包含的引用类型属性将被所有实例共享;
- 子类在实例化的时候不能给父类构造函数传参;
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | function Animal() {this.name = 'animal'
 this.type = [0, 1]
 }
 
 function Dog() {
 this.eat = 'eat shit'
 }
 
 Dog.prototype = new Animal()
 
 const dog1 = new Dog()
 const dog2 = new Dog()
 
 dog1.type.push(2)
 
 console.log(dog1.type)
 console.log(dog2.type)
 
 | 
2.借用构造函数继承
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 
 | function Animal() {this.name = 'animal'
 this.type = [0, 1]
 }
 
 function Dog() {
 Animal.call(this)
 this.eat = 'eat shit'
 }
 
 Dog.prototype = new Animal()
 
 const dog1 = new Dog()
 const dog2 = new Dog()
 
 dog1.type.push(2)
 
 console.log(dog1.type)
 console.log(dog2.type)
 
 
 | 
核心代码是Animal.call(this),在创建子类Dog的实例时调用父类Animal的构造函数,这样就会把父类的每个属性都给子类复制一份
缺点:
- 只能继承父类的实例属性和方法,不能继承原型属性/方法(因为它只执行了父类构造函数里的内容)
- 无法实现复用,每个子类都有父类实例函数的副本,影响性能
3.组合继承
组合上述两种方法就是组合继承。用原型链实现对原型属性和方法的继承,用借用构造函数技术来实现实例属性的继承。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 
 | function Animal(name) {this.name = name
 this.type = [0, 1]
 }
 
 function Dog(name) {
 Animal.call(this, name)
 this.eat = 'eat shit'
 }
 
 Animal.prototype.getName = function() {
 return this.name
 }
 
 Dog.prototype = new Animal()
 Dog.prototype.constructor = Dog
 
 const dog1 = new Dog('王一琳')
 const dog2 = new Dog('刘贝利')
 
 dog1.type.push(2)
 
 console.log(dog1.type)
 console.log(dog2.type)
 console.log(dog1.eat)
 console.log(dog2.eat)
 console.log(dog1.getName())
 console.log(dog2.getName())
 
 
 | 

缺点:可以清楚的看到,其实我们的代码里调用了两次Animal(),所以其实例中会存在原型对象中的所有属性,造成冗余。
4.原型式继承
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | function clone(obj){function F(){}
 F.prototype = obj;
 return new F();
 }
 
 
 
 Object.create(obj)
 
 
 | 
利用一个空构造函数作为媒介,完成对一个对象的浅拷贝
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | const dog = {name: '王一琳',
 friends: ['王二琳', '王三琳']
 }
 
 const newDog1 = clone(dog)
 newDog1.name = '鸡公'
 newDog1.friends.push('王四琳')
 
 const newDog2 = clone(dog)
 newDog2.name = '鸡夫'
 newDog1.friends.push('王五琳')
 
 console.log(newDog1.name)
 console.log(newDog2.name)
 
 console.log(newDog1.friends)
 console.log(newDog2.friends)
 
 | 
缺点:
- 原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
- 无法传递参数
5.寄生式继承
核心:在原型式继承的基础上,增强对象(新增属性方法),返回返回这个对象
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | function createNew(obj){
 const clone = Object.create(obj)
 clone.sayHi = function() {
 console.log('你好')
 }
 return clone
 }
 
 const dog = {
 name: '王一琳',
 friends: ['王二琳', '王三琳']
 }
 
 const newDog = createNew(dog)
 
 console.log(newDog.name)
 console.log(newDog.friends)
 newDog.sayHi()
 
 | 
缺点(同原型式继承):
- 原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
- 无法传递参数
6.寄生组合继承
结合借用构造函数传递参数和寄生模式实现继承
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | function inheritPrototype(Son, Father) {const prototype = Object.create(Father.prototype)
 prototype.constructor = Son
 Son.prototype = prototype
 }
 
 function Animal(name) {
 this.name = name
 this.type = [0, 1]
 }
 
 Animal.prototype.sayHi = function() {
 console.log('你好')
 }
 
 function Dog(name, eat) {
 this.name = name
 this.eat = eat
 }
 
 inheritPrototype(Dog, Animal)
 
 const dog1 = new Dog('鸡公', 'eat shit')
 const dog2 = new Dog('王一琳', 'eat more shit')
 
 | 

这是最成熟的方法,也是现在库实现的方法
与组合继承相比,寄生组合继承只调用了一次父类Animal,因此避免了在Animal.protoype上创建多余的属性。于此同时,原型链还能保持不变;因此,还能够正常使用instanceof 和isPrototypeOf()
7.class继承
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | class Animal {constructor(name) {
 this.name = name
 }
 getName() {
 return this.name
 }
 }
 class Dog extends Animal {
 constructor(name, age) {
 super(name)
 this.age = age
 }
 }
 
 |