1.原型链继承
原型链继承最重要的点就是子类的原型是父类的实例
function Person(){
this.name = "xiaoming"
}
Person.prototype.getName = function(){
console.log(this.name)
}
function Child(){}
Child.prototype = new Person()
let child1 = new Child()
child1.getName()//xiaoming
原型链继承有一定的缺点,比如下面一段代码
function Person() {
this.name = 'xiaming'
this.colors= ['red','blue']
}
Person.prototype.getColors = function(){
console.log(this.colors)
}
function Child(){}
Child.prototype = new Person()
let child1 = new Child()
let child2 = new Child()
child1.colors.push('yellow')
child1.getColors()
child2.getColors()
//[ 'red', 'blue', 'yellow' ]
[ 'red', 'blue', 'yellow' ]
这是因为引用类型的属性被所有实例共享
还有缺点就是像这些代码,创建Child实例时,无法向Person传参
2.借用构造函数继承(经典继承)
经典继承就可以解决上面的问题
function Person() {
this.name = 'xiaoming'
this.colors = ['red', 'blue']
this.getName = function () {
console.log(this.name);
}
}
function Child() {
Person.call(this)
}
let child1 = new Child()
let child2 = new Child()
child1.colors.push('yellow')
console.log(child1.colors);
console.log(child2.colors);
//[ 'red', 'blue', 'yellow' ]
[ 'red', 'blue' ]
这样不还是没有办法传参?改造一下
function Person(name) {
this.name = name
this.colors = ['red', 'blue']
this.getName = function () {
console.log(this.name);
}
}
function Child(name) {
Person.call(this,name)
}
let child1 = new Child('xiaoming')
let child2 = new Child('zhangsan')
console.log(child1.name);
console.log(child2.name);
//xiaoming
zhangsan
//这样就可以实现传参了
3.组合继承
结合 原型链继承 借用改造函数继承 思路:使用原型链实现对原型方法的继承,通过借用构造函数实现对实例属性的继承
function Person(name) {
this.name = name
this.colors = ['red','blue']
}
Person.prototype.getName = function () {
console.log(this.name);
}
function Child(name) {
Person.call(this,name)
}
Child.prototype = new Person()
let child1 = new Child('xiaoming')
let child2 = new Child('zhangsan')
child1.getName()
child2.getName()
console.log(child1 instanceof Child);
console.log(child1 instanceof Person);
//运行结果
// xiaoming
//zhangsan
//true
//true
这是最常用的继承方式,缺点就是无论什么情况下都会调用两次父类的构造函数
4.原型式继承
function CreateObj(o) {
function F() { }
F.prototype = o
return new F()
}
let person = { name: 'xiaoming', friend: ['sss', 'ddd'] }
let person1 = CreateObj(person)
let person2 = CreateObj(person)
console.log(person1.name);
console.log(person2.name);
person1.name = 'xiaomei'
console.log(person1.name);
console.log(person2.name);
console.log(person.name);
//xiaoming
//xiaoming
//xiaomei
//这里name属性没有修改是因为是吧name属性直接赋值给person1并没有到原型链上找
person1.friend.push('ffff')
console.log(person1.friend);
console.log(person2.friend);
//[ 'sss', 'ddd', 'ffff' ]
//[ 'sss', 'ddd', 'ffff' ]
//引用类型是共享的
5.寄生式继承
let obj = {
name: 'xiaopao',
friends:['lll','sss']
}
function CreateObj(o) {
function F() { }
F.prototype = o
return new F()
}
function CreateObjPlus(o) {
let newObj = CreateObj(o)
newObj.sayName = function () {
console.log(this.name);
}
return newObj
}
let obj1 = CreateObjPlus(obj)
let obj2 = CreateObjPlus(obj)
console.log(obj1.name);
obj1.sayName()
obj2.friends.push('ggg')
console.log(obj1.friends);
console.log(obj2.friends);
缺点:1、跟借用构造函数一样,每次创建对象都会创建一遍方法;2、子类实例会共享父类引用类型属性
6.寄生组合式继承 以子类构造函数作为桥梁,将父类原型上的属性挂在子类构造函数的原型上
function Animal(name, age) {
this.name = name
this.age = age
this.hobbies = ['sss','ddd']
}
Animal.prototype.run = function () {
console.log('运动');
}
function Dog(name, age,dd) {
Animal.call(this, name, age)
this.eat = '吃骨头'
}
function F() { }
F.prototype = Animal.prototype
Dog.prototype = new F() //这里Dog.prototype.constructor已经指向了创建的实例
console.log(Dog.prototype.constructor);
//这里因为执行Dog里面的函数 所以实际是由Animal创建的实例
//所以这里打印为Animal所以要执行以下代码将constructor指回Dog,这才算完全的继承
//Dog只是最为中介链接Animal与最终实例的桥梁
Dog.prototype.constructor = Dog
//这里可以封装成函数
function linkConstructor(child,parent) { //效果一样
function F() { }
F.prototype = parent.prototype
child.prototype = new F()
child.prototype.constructor = child
}
let d1 = new Dog('富贵', 1)
console.log(d1); // { name: '富贵', age: 1, hobbies: [ 'sss', 'ddd' ] }