> ## **继承** - **类的继承** 继承的基本原理就是原型链,继承的本质就是原型链,如果没有原型链在JS中是很难实现继承的。 **继承的方式** 1.构造函数实现继承 代码 ``` /* 将父级的构造函数this指向,指到子构造函数的实例上去,这样在子类中就可以看到父类的属性和方法 */ function Parent1() { this.name = 'parent1' } function Child1() { Parent1.call(this) // 把父级parent1构造函数在子类的构造函数里面执行的话同时修改了父级构造函数的this指向,也就是指向了child1这个类实例化的对象的引用。说白了就是this指向从而导致父类执行的时候这个属性都会挂在到child1这个类的实例上去 this.type = 'child1' } console.log(new Child1) ``` 结果: ![](https://i.loli.net/2019/05/21/5ce3b26367a8480907.png) 缺点: parent1除了构造函数里面的内容,它原型链上的东西并未继承,只是实现了部分继承 代码: ``` /* 将父级的构造函数this指向,指到子构造函数的实例上去,这样在子类中就可以看到父类的属性和方法 */ function Parent1() { this.name = 'parent1' } Parent1.prototype.say = function() {} // 不能被Child1继承 function Child1() { Parent1.call(this) // 把父级parent1构造函数在子类的构造函数里面执行的话同时修改了父级构造函数的this指向,也就是指向了child1这个类实例化的对象的引用。说白了就是this指向从而导致父类执行的时候这个属性都会挂在到child1这个类的实例上去 this.type = 'child1' } console.log(new Child1) ``` 结果: ![](https://i.loli.net/2019/05/21/5ce3ba1099ca557711.png) 2.原型链继承 代码 ``` /* prototype是自雷Child2的一个属性,每个函数都是有这个属性的,这个属性是一个对象,对象是可以任意赋值的,接下来把Parent2的实例赋值给子类的prototype属性,现在实例化了Child2,new Child2生成的是一个新的对象,这个对象上有一个.\_proto\_,这个属性就等于Child2.prototype,所以就继承成功了 */ function Parent2() { this.name = 'parent2' } function Child2() { this.type = 'child2' } Child2.prototype = new Parent2() console.log(new Child2) ``` 结果 ![](https://i.loli.net/2019/05/21/5ce3bd8005eaf72757.png) new Child2().__proto__ === Child2.prototype,而Child2.prototype又等于父类的实例,所以可以成功继承父类 ![](https://i.loli.net/2019/05/21/5ce3bdef38cda44464.png) 缺点 ``` function Parent2() { this.name = 'parent2' this.play = [1,2,3] } function Child2() { this.type = 'child2' } Child2.prototype = new Parent2() var s1 = new Child2() var s2 = new Child2() console.log(s1.play, s2.play) // 没毛病 s1.play.push(4) // 重新赋值 console.log(s1.play, s2.play) // 结果?都变化了,不是我们想要的,这两个应该是隔离的 ``` 说明 ![](https://i.loli.net/2019/05/21/5ce3c0b75516714535.png) 3.组合方式 - 通用推荐 代码: ``` /* 这种方式就可以把前面那两种继承方式的缺点弥补了 */ function Parent3() { this.name = 'parent3' this.play = [1,2,3] } function Child3() { Parent3.call(this) this.type = 'child3' } Child3.prototype = new Parent3() var s3 = new Child3() var s4 = new Child3() s3.play.push(4) console.log(s3.play, s4.play) //  [1, 2, 3, 4], [1, 2, 3],互不影响 ``` 缺点 ``` // 父类执行了两遍,性能不优 Parent3.call(this) // 执行了一遍 Child3.prototype = new Parent3() // 也执行了一遍 ``` 改进 ``` function Parent3() { this.name = 'parent3' this.play = [1,2,3] } function Child3() { Parent3.call(this) this.type = 'child3' } Child3.prototype = Parent3.prototype // 优化 var s3 = new Child3() var s4 = new Child3() s3.play.push(4) console.log(s3.play, s4.play) //  [1, 2, 3, 4], [1, 2, 3],互不影响 构造函数体内,通过两个函数组合,能拿到所有的,构造函数的属性和方法,既然是想继承父类的原型对象,所以直接赋值给当前的原型对象就行了,这样就可以把父类函数里的属性和方法拿到包括原型对象,这样就可以实现继承了。 这个时候就只实例化一次了只有Parent3.call(this)这个时候父类执行一次,原型对象上只是一个简单的引用,,JS的数据类型只有值类型和引用类型,Child3.prototype = Parent3.prototype 这里都是对象肯定都是引用类型,所以这块不会再执行父类的构造函数,这是它的一个优点。 ```