原生JS继承详解
参考资料
不要讲什么设计模式 , 看得老子脑壳昏 , 智商欠费 , 还是简单点 , 方案 , 继承 , 就完事了
// 首先我们得有个父类
// 这个父类也很简单
function Human(name) {
this.name = name || '肥宅' // 有参就取参 , 没参就叫肥宅
}
Human.prototype.drink = function(drinks) {
console.log(this.name + '正在喝' + (drinks || '可乐'));
}
复制代码
OK, 我们现在有了一个父类, 先来看一个最简单的继承
最简单的原型链继承
直接把父类的实例作为子类的原型
// 先写个空类
function Jianren(){}
Jianren.prototype = new Human()
// new个实例看看
var haidong = new Jianren();
console.log(haidong.name); //'肥宅'
console.log(haidong.drink()); //'haidong正在喝可乐'
复制代码
对于这个继承, 概括为子类的原型链绑定父类的实例实现继承
缺陷
- 不能实现多类继承
- 为子类新增属性和方法,必须要在new Animal()这样的语句之后执行
- 无法控制子类能够访问父类属性的权限
- 创建子类的实例时也不能向父类构造函数传参
构造继承
复制父类属性
function Jianren(){
Human.call(this);// 把父类写进子类构造函数内部, 调用call把子类的this传进去改变this的指向做到复制父类属性
this.age = 22
}
复制代码
- 构造继承其实就是复制了父类属性, new Jianren()出来的实例的construct和prototype都与Human无关, 指向Jianren()
- 可以不管new 父类前后顺序来新增子类的属性
- 创建子类实例时, 也可以向父类传递参数,
- 可以call多个父类实现多类继承
- 只能继承父类的实例属性和方法, 不能继承/访问到父类原型上的东西
- 难以复用, 影响性能
实例继承
在子类内部添加一个属性, 该属性为父类的实例
function Jianren(name){
var ren = new Human();// 注意这里是不是this
ren.name = name || 'haidong';
return ren;
}
复制代码
- 不会限制调用的方式, new 子类() 或者 子类()执行, 都返回ren
- new Jianren()的实例实际上是new Human(), constructor也是指向Human()
- 不支持多类继承
拷贝继承
function Jianren(name){
var ren = new Human()
for(var r in ren) {
Cat.prototype[r] = ren[r] // 该处r是key, 应该用Object[key]的形式赋值和访问
}
Jianren.prototype.name = name || 'haidong'
}
复制代码
- 支持多继承
- 效率低, 拷贝属性会占用内存
- 不可枚举父类
推荐的两种继承
组合继承和寄生组合继承
上面的继承主要是帮助理解, 或有时暴力快速解决小问题时使用 .
推荐一, 组合继承
子类的原型指向父类实例,该父类实例又可向上原型查找访问,修正后constructor指向子类自身
function Jianren(name) {
Human.call(this) //第一步拷贝属性
this.name = name || 'haidong'
}
Jianren.prototype = new Human() //第二步, 原型指向父类实例
Jianren.prototype.constructor = Jianren //第三步, 修正constructor指向
复制代码
- 可以继承实例的属性/方法, 也可以继承原型的属性方法,
- 是子类的实例, 也相当于是父类的实例
- 子类的原型指向父类实例,该父类实例又可向上原型查找访问,修正后constructor指向子类自身
- 可传参,//call()可以传参,具体用法,自行百度谷歌
- 调用两次父类构造函数, 占内存, 第一步拷贝过一次, 第二步导致以后的Jianren的实例里和Jianren原型上重复的属性
推荐二, 寄生组合继承
扫描二维码关注公众号,回复:
4081008 查看本文章
// 有点复杂
function Jianren(name) {
Human.call(this) //第一步拷贝属性
this.name = name || 'haidong'
}
(function() { //来个匿名立即执行函数
function J() {}// 创建一个中间类, 空, 什么都没有
J.prototype = Human.prototype //第二步中间类原型指向父类原型
Jianren.prototype = new J() //子类原型赋值为中间类的实例,往上查找,指向了父类的原型
})()
Jianren.prototype.constructor = Jianren //修正子类constructor指向
复制代码
与上面的组合继承相比, 第一步拷贝 和 第二步中间类(空类)不会重复有Human的属性, 略微复杂, 可以忽略不计, 完美方式