js继承

117 阅读2分钟

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' ] }