谈谈JavaScript中继承方式

聊一聊js中的继承

 

一、简单继承---使用原型赋值的方式继承,将实例化的对象,赋值给子级的原型
父级构造函数
function Parent(param) {
    this.name = 'parent'
    this.otherName = 'otherName'
    this.param = param || 'param'
}
Parent.prototype.sayName = function() {
    console.log('this.name :', this.name)
}
子级构造函数
function Son() {
    this.name = 'son'
}
Son.prototype = new Parent() // 继承


let newSon1 = new Son()
let newSon2 = new Son()

console.log('newSon1 :', newSon1)                   // newSon1 : Parent { name: 'son' }
// newSon1 : Parent { name: 'parent', otherName: 'otherName' } // newSon1.__proto__ 指向构造函数 Son 的原型 prototype
console.log('newSon1 :', newSon1.__proto__)         
// newSon1.__proto__ 指向构造函数 Son 的原型 prototype
// Son 的 prototype 赋值为 new Parent(),所以 Son 的构造函数不是自己,而是 Parent
// newSon1.__proto__.constructor : function Parent() {
//     this.name = 'parent'
//     this.otherName = 'otherName'
// }
console.log('newSon1.__proto__.constructor :', newSon1.__proto__.constructor)         

console.log('newSon2 :', newSon2)                   // newSon1 : Parent { name: 'son' }
// newSon2 : Parent { name: 'parent', otherName: 'otherName' } // newSon2.__proto__ 指向构造函数 Son 的原型 prototype
console.log('newSon2 :', newSon2.__proto__)         

// 修改 newSon1 继承而来的 otherName 属性, newSon2也发生了变化
newSon1.__proto__.otherName = 'son1-OtherName'
console.log('newSon1.otherName :', newSon1.otherName)
console.log('newSon2.otherName :', newSon2.otherName)

缺点:

所有的实例化对象会共享属性,对于任一个属性的修改,其他的实例化对象都会同步变化
子级的构造函数也不是自己,需要重新指定

 

二、经典继承---改变this指向到当前的实例化对象
父级构造函数
function Parent() {
    this.name = 'parent'
    this.otherName = 'otherName'
}
Parent.prototype.sayName = function() {
    console.log('this.name :', this.name)
}
子级构造函数
function Son(param) {
    Parent.call(this, param) // 继承
}
缺点:
解决了上面提到的属性共享问题
新问题出来了,需要将方法定义在构造函数内部,否则无法被继承,
每实例化一次,构造函数里面的函数方法就创建一次,对资源是一个浪费

 

三、组合继承
使用call/apply方法进行属性的继承
用原型链继承的方法,继承原来的所有函数方法
父级构造函数
function Parent() {
    this.name = 'parent'
    this.otherName = 'otherName'
}
Parent.prototype.sayName = function() {
    console.log('this.name :', this.name)
}
子级构造函数
function Son(param) {
    Parent.call(this, param)
}
Son.prototype = Parent.prototype  
Son.prototype.constructor = Son // 修改构造函数为自己
缺点:
因为是使用赋值引用的模式进行原型继承,
如果子级重写父级的某个属性或方法,父级也会发生改变

 

四、组合继承+深拷贝
父级构造函数
function Parent() {
    this.name = 'parent'
    this.otherName = 'otherName'
}
Parent.prototype.sayName = function() {
    console.log('this.name :', this.name)
}
子级构造函数
function Son(param) {
    Parent.call(this, param)
}
深拷贝原型链
deepCopy(Son.prototype, Parent.prototype)
Son.prototype.constructor = Son // 修改构造函数为自己
function deepCopy(target, obj) {
    for (const prop in obj) {
        if (obj.hasOwnProperty(prop)) {
            if (typeof obj[prop] === 'object') {
                target[prop] = Object.prototype.toString.call(obj[prop]) === '[object Array]' ? [] : {}
                deepCopy(target[prop], obj[prop])
            } else {
                target[prop] = obj[prop]
            }            
        }
    }
}

 

posted @ 2019-04-29 17:09  青S衫%  阅读(212)  评论(0)    收藏  举报