聊一聊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] } } } }