继承:继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等。在JavaScript中实现继承的过程中要注意对引用数据类型的处理和对原型上的属性和方法的继承。
一、原型链的继承
核心就是将子类的prototype指向父类的实例
function Parent(name){
this.name = name
this.friends = ['f1','f2']
}
Parent.prototype.say = function(){
console.log(`我朋友有${this.friends}`)
}
function Son(school) {
this.school = school
}
Son.prototype = new Parent()
var s1 = new Son('光明小学')
var s2 = new Son('武当小学')
s1.friends.push('f*')
s1.say()
s2.say()
此时两个打印的都是我朋友有f1,f2,f*
这显然不是我们想要的结构,继承之后的每个属性应该是属于每个子类对象的不应该出现互相干扰才对。
此种方法有点就是简单容易实现。
缺点:1、共享了父类的属性,2、new Parent()
多执行了父类的构造器函数,3、创建子类对象的时候没法给父类传参(Parent的name无法进行赋值)
二、构造函数的继承
核心:在子类中利用call方法实现父类继承
function Parent(name,age){
this.name = name
this.age = age
this.friends = ['f1','f2']
this.hello = function() {
console.log(`我是${this.name}`)
}
this.sayFriends = function() {
console.log(`我的朋友是${this.friends}`)
}
}
Parent.prototype.say = function() {
console.log('say')
}
function Son(name, age, school) {
Parent.call(this, name, age)
}
var p1 = new Son('张三', 12)
var p2 = new Son('李四', 13)
p1.friends.push('f*')
p1.sayFriends() //我的朋友是f1,f2,f*
p2.sayFriends() //我的朋友是f1,f2
p1.hello() //我是张三
p1.say() //p1.say is not a function
以上的打印可以看出这种方式可以实现子类对象属性的独立,但是父类原型上的方法属性子类是访问不到的。
那么以上两种方式结合在一起正好互补了不足之处。
三、原型加构造函数的继承
function Son(name, age) {
Person.call(this, ...arguments)
}
Son.prototype = Parent.prototype
这样将Son的原型直接指向Parent的原型,Son就可以访问父类原型上的方法了,但是会出现下面这个问题Son的构造函数变成了Parent这是不对的Son的构造函数应该是Son才对。所以就有了最后的寄生组合继承。

四、寄生组合继承
核心:利用Object.create创建一个以父类为原型的对象作为子类的原型
function Son(name, age) {
Person.call(this, ...arguments)
}
Son.prototype = Object.create(Parent.prototype);
Son.prototype.constructor = Son;
该方法应该是最为完善的方案。
五、利用ES6 extends
class Person {
//调用类的构造方法
constructor(name, age) {
this.name = name
this.age = age
}
}
class Son extends Person {
constructor(name, age, school) {
super(name, age)//通过super调用父类的构造方法
this.school = school
}
}
这个方法代码的可读性比较高,代码更加的好看。extends的实现原理还请各路大神在评论留言。