继承
原型链继承
基本思路:利用原型让一个引用类型继承另一个引用类型的属性和方法
原型和构造函数和实例之间的关系
- 每个构造函数都有一个原型对象
- 原型对象都有一个指针指向构造函数
- 实例都包含一个指向原型对象的内部指针
function Father (){
this.name = "Father"
this.friends = ["Jack","May"]
}
Father.prototype.sayName = function(){
console.log(this.name)
}
function Son (){
this.name = "Son"
}
//子类的原型指向父类的实例
Son.prototype = new Father()
Son.prototype.sayName = function(){
console.log(this.name)
}
const testSon = new Son()
testSon.sayName()//"Son"
console.log(testSon.friends)//"Jack","May"
testSon.friends.push("Sonfriend-Jone")
const testSon1 = new Son()
console.log(testSon.friends)//"Jack","May","Sonfriend-Jone"
console.log(testSon1.friends)//"Jack","May","Sonfriend-Jone"
原型链继承存在的问题:
- 所有实例都共享一个原型,导致引用类型的数据会被共享,实例修改的引用数据来自原型,一旦修改,改的是原型中的引用数据,会导致其他对象也会受到牵连,也就是说,失去了私有的来自继承的变量
- 创建子类的时候,不能向父类去传参
借用构造函数继承
基本思路:在子类中去调用父类的构造函数,通过apply和call在将来新建的对象上执行构造函数
function Father(){
this.friends = ["Jack-F","May-F"]
}
function Son(){
//继承了Father的属性
Father.call(this)
}
const testSon = new Son()
testSon.friends.push("Jone-S")
console.log(testSon.friends)//"Jack-F","May-F","Jone-S"
const testSon2 = new Son()
console.log(testSon2.friends) //"Jack-F","May-F"
此时可以看到,引用类型形成了自己的私有变量
另外还可以使用apply,向父类构造函数进行传参
function Father(name){
this.name = name
}
function Son(){
Father.call(this,"Son1")
this.age = 18
}
const testSon = new Son()
console.log(testSon.name,testSon.age)//Son1 18
但是这种模式也有问题,它仅仅是借壳生产,在原型层面是并没有让Son和Father进行任何的联系,这就意味着我们不能像原型继承一样,对一些可以提到公共领域(原型)的方法进行复用,方法只能在构造函数中定义
组合式继承
基本思路:结合原型链继承和借用构造函数继承,使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现自己的私有属性
function Father(name){
this.name = name
this.friends = ["Jack-F","May-F"]
}
Father.prototype.sayName = function (){
console.log(this.name)
}
function Son(name,age){
Father.call(this,name)
this.age = age
}
//通过原型建立继承关系
Son.prototype = new Father()
console.log(Son.prototype.constructor)//此时构造函数改变了!!!!!
Son.prototype.constructor = Son//所以我们要把构造函数改回来
//添加公用方法
Son.prototype.sayAge = function(){
console.log(this.age)
}
const son1 = new Son("son1",16)
son1.friends.push("Mike-S1")
console.log(son1.friends)
son1.sayName()
son1.sayAge()
const son2 = new Son("son2",17)
console.log(son2.friends)
son2.sayName()
son2.sayAge()
原型式继承
基本思路:假设我已经有了一个对象,那么我可以通过这个对象来构建一个新对象,并且让他人我做爸爸
//o为已有对象
function Object(o){
function F(){}
F.prototype = o
return new F()
}
const father = {
name:"Jack",
friends:["May-F","Mike-F"]
}
const son = Object(father)
console.log(son.name)//Jack
console.log(son.friends) //"May-F","Mike-F"
son.name = "pekey"
son.friends.push("Rob-S1")
console.log(son.name)//pekey
console.log(son.friends) //"May-F","Mike-F","Rob"
const son2 = Object(father)
son2.name="Lida"
son2.friends.push("James-S2")
console.log(son2.name)//Lida
console.log(son2.friends)//"May-F","Mike-F","Rob","James-S2"
可以使用Object.create来代替上述的object
可以看到我们利用father对象来构造出一个son对象,而且实现了原型继承,但是对于引用类型也是会出现没有形成私有的情况
寄生式继承
基本思路:利用原型式继承生成一个下级对象,然后让该对象去拥有属于自己的方法和属性
function Object(o){
function F(){}
F.prototype = o
return new F()
}
const father = {
name:"Jack",
friends:["May-F","Mike-F"]
}
//这一步相当于让原型式继承出来的函数包装自己的属性和方法
function create(o){
const clone = Object(o)
clone.say = function(){
console.log("Hi")
}
return clone
}
const son = create(father)
son.say()
寄生组合式继承
由于组合式继承调用了两次父类构造函数,第一次是在创建子类原型的时候,第二次在子内借用构造函数的时候,有没有什么方法只调用一次?
在第一次调用时,我们要指定子类的原型,所以调用父类构造函数生成一个实例对象,这一步时可以省略的
function Object(o){
function F(){}
F.prototype = o
return new F()
}
function inherit(Son,Father){
const prototype = Object(Father.prototype)//既然我们使用的是父类的原型对象,那么我们没有必要使用实例化对象,直接拿原型对象来构造子类的实例对象
prototype.constructor = Son
Son.prototype = prototype
}
function Father(name){
this.name = name
this.friends = ["Jack-F","May-F"]
this.home = "A street"
}
Father.prototype.sayName = function(){
console.log(this.name)
}
function Son(name,age){
Father.call(this,name)
this.age = age
}
inherit(Son,Father)
Son.prototype.sayAge = function(){
console.log(this.age)
}
const testSon1 = new Son("James",18)
testSon1.sayName()
testSon1.sayAge()
console.log(testSon1.friends)
testSon1.friends.push("Linda-S1")
testSon1.home = "B street"
console.log(testSon1.home)
const testSon2 = new Son("Jimy",17)
testSon2.sayName()
testSon2.sayAge()
console.log(testSon1.friends)
console.log(testSon2.home)