前言
如果没有class的话,js实现手动继承的方式有很多,其中有很多简单能用的,也有不太好用,这里介绍一个比较常见到的寄生组合继承的实现方式
当然更厉害的可以尝试直接更细腻的模拟js的class或者其他编程语言的class,写出更完整的继承效果,到那里篇章更复杂更难理解了细节多到数不胜数(例如很多语法糖的处理),这里只介绍原型相对比较常见的利用原型实现写法
手动实现继承
我们利用现有原型天然的支持继承效果,也就是调用函数时会从函数到原型查找方法,找不到会将原型看做对象,继续找其构造函数的原型,向上调用直到原型为空结束
也就是我们要做的实际上也就是,更方便的将继承效果在创建子类的时候,一键挂载关联到子类
实现大概有下面几个步骤:
- 直接将父原型给子原型,那么他们会共享,也不太符合原型的调用链,出现公用混乱问题
- 因此采用新建寄生对象的方式,创建一个新的原型对象(寄生对象),将父类的原型赋予寄生对象原型上,寄生对象的构造函数指向子类,将子对象的原型指向寄生对象,这样寄生对象成功伪装成我们的新子类了
- 子类初始化的时候可根据情况调用父类构造函数避免出现属性丢失问题,使用callback无缝调用
- 父子类建立关联后,在扩展子原型链条,相当于在给我们的寄生对象扩展属性,也就没有侵入到父原型了
ps:直接new Parent()给子原型不挺好,这样相当于多调用了一次父构造函数,看起来比较多余,且构造内容多的话,也可能会间接导致子原型函数出现了不该出现的函数(且返回内容为空)
js
//寄生对象
function objectFactory(prototype) {
function F() {}
F.prototype = prototype;
return new F();
}
//寄生对象化作继承者
function createAssociation(Children, Parent) {
const prototype = objectFactory(Parent.prototype);
prototype.constructor = Children;
Children.prototype = prototype;
}
//父类函数
function Parent(name) {
this.name = name;
}
Parent.prototype.getMyName = function() {
return `name:${this.name}`;
}
//子类函数
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
//父子建立关联,此时子原型会被覆盖,注意顺序
createAssociation(Child, Parent);
//开始扩展子原型
Child.prototype.getMyAge = function() {
return `age:${this.age}`;
}
const child1 = new Child('child1', 10);
const child2 = new Child('child2', 20);
console.log(child1.getMyName()); // name:child1
console.log(child1.getMyAge()); // age:10
console.log(child2.getMyName()); // name:child2
console.log(child2.getMyAge()); // age:20
就这样我们实现了继承
最后
如果让你实现一个带 super 的效果,你该怎么实现呢,有什么思路么(当然不必非得一样😄)