今天我们来说一下构造函数的继承都有哪些方式以及优缺点是什么?
第一种:原型链继承
<script>
//先声明父类的构造函数
function Father(name) {
this.name = name
this.list = ['a']
}
Father.prototype.sayHi = function () {
console.log('hello')
}
//声明子类的构造函数
function Son() {
}
Son.prototype = new Father()
//创建两个实例对象
let xiaoming = new Son('小明')
let xiaobai = new Son('小白')
xiaoming.sayHi() //方法也可以正常使用
console.log(xiaoming); // 输出{} 并没有输出{name:'小明'}
console.log(xiaobai); // 输出{} 并没有输出{name:'小白'}
//写到这里你会发现缺点1.无法向父类构造函数传参
//然后我们往list添加一个字符串b
xiaoming.list.push('b')
console.log(xiaoming.list) //输出[a,b]
console.log(xiaobai.list) //输出[a,b]
/*
这时候我们发现所有实例都被影响到了
这是因为执行xiaoming.list的时候,由于xiaoming实例身上并没有list这个属性,所以往xiaoming实例的原型上找,由于是原
型链继承,所以Son的原型继承了Fahter构造函数new出来的实例的属性和方法,所以修改的也是原型上的属性list ,由于
xiaoming和xiaobai指向的是同一个原型,所以导致被修改,
这时候我们发现第缺点2.若父类构造函数共有属性为引用类型,原型上属性的改变会作用到所有的实例上
*/
</script>
第二种:借用构造函数继承
<script>
//先声明父类的构造函数
function Father(name) {
this.name = name
this.list = ['a']
}
Father.prototype.sayHi = function () {
console.log('hello')
}
//声明子类的构造函数
function Son(name) {
//使用call方法,call方法可以改变this的指向并且立即执行该函数
Father.call(this, name)
}
//创建两个实例对象
let xiaoming = new Son('小明')
let xiaobai = new Son('小白')
xiaoming.sayHi() //但是我们又发现了一个新缺点:1.父类构造函数原型上的方法已经没办法使用了,请自行注释
console.log(xiaoming); // 输出{name:'小明'}
console.log(xiaobai); // 输出{name:'小白'}
//写到这里你会发现上面原型链继承的缺点1.无法向父类构造函数传参已经可以解决了
//然后我们往list添加一个字符串b
xiaoming.list.push('b')
console.log(xiaoming.list) //输出[a,b]
console.log(xiaobai.list) //输出[a]
//发现属性没有私有化也可以解决了
</script>
第三种:原型链+构造函数继承
//先声明父类的构造函数
function Father(name) {
//加了这句代码后我们发现被输出了3次,分别是new Father的时候1次,创建两个实例的时候输出了2次,所以我们还可以进行
优化,我们来看方法4
console.log('father输出了')
this.name = name
this.list = ['a']
}
Father.prototype.sayHi = function () {
console.log('hello')
}
//声明子类的构造函数
function Son(name) {
//使用call方法,call方法可以改变this的指向并且立即执行该函数
Father.call(this, name)
}
Son.prototype = new Father() //原型链继承
//创建两个实例对象
let xiaoming = new Son('小明')
let xiaobai = new Son('小白')
xiaoming.sayHi() //方法可以正常调用
console.log(xiaoming); // 输出{name:'小明'}
console.log(xiaobai); // 输出{name:'小白'}
//可以向父类构造函数传参
//然后我们往list添加一个字符串b
xiaoming.list.push('b')
console.log(xiaoming.list) //输出[a,b]
console.log(xiaobai.list) //输出[a]
//属性也实现了私有化
第四种:寄生继承
1.为了方便看,多余的代码我们就删除了
//先声明父类的构造函数
function Father(name) {
console.log('father输出了')
this.name = name
}
Father.prototype.sayHi = function () {
console.log('hello')
}
function Son(name) {
Father.call(this, name)
}
Son.prototype = Object.create(Father.prototype)//寄生继承
//这时候就不会因为给Son的prototype继承的时候,多调用一次
// Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的原型
let xiaoming = new Son('小明')
xiaoming.sayHi()
console.log(xiaoming);