下一篇:JS实现继承的方式ES6版
实现继承的方式
类的定义
function People(name){
//属性
this.name = name || Annie
//实例方法
this.sleep=function(){
console.log(this.name + '正在睡觉')
}
}
//原型方法
People.prototype.eat = function(food){
console.log(this.name + '正在吃:' + food);
}
1 原型链继承:将子类构造函数的原型指向父类的一个实例。
function Parent() {
this.name = "Parent";
}
Parent.prototype.say = function() {
return this.name;
}
function Child() {
this.childName = "Child";
}
Child.prototype = new Parent();
const child1 = new Child()
console.log(child1.say())
此时子类的constructor指向了父类。
优点:
实现简单。
缺点:
创建子类实例的时候不能向父类传参。
父类的引用数据类型属性被子类实例修改后,所有的子类实例上的该属性值也会跟着被修改。
2 借用构造函数继承:在子类构造函数里调用父类构造函数并传参,使用 call 改变 this 指向。
function Parent(name, age) {
this.name = name;
}
function Child(name, age) {
father.call(this, name, age)
}
const child1 = new Child('name', 12)
优点:
解决了子类实例共享父类引用属性的问题。
创建子类实例时,可以向父类构造函数传参。
可以实现多继承(call多个)。
缺点:
无法继承父类原型中的方法。(除非父类的方法写入构造函数中,每次创建实例都会创建一遍方法,函数复用就无从谈起了)
3 组合继承:将原型链和借用构造函数的技术结合到一起。
背后思想是使用原型链实现对方法的继承,通过借用构造函数实现对实例属性的继承。这样,既能够保证能够通过原型定义的方法实现函数复用,又能够保证每个实例有自己的属性。
function Parent(name, age) {
this.name = name;
this.age = age;
this.colors = ['red', 'green']
}
Parent.prototype.say = function() {
return this.name;
}
function Child(name, age, grade) {
Parent.call(this, name, age);// 创建子类实例时会执行一次
this.grade = grade;
}
Child.prototype = new Parent(); // 指定子类原型会执行一次
Child.prototype.constructor = Child;// 校正构造函数
优点:
组合继承既具有原型链继承能够复用函数的特性,又有借用构造函数方式能够保证每个子类实例能够拥有自己的属性以及向超类传参的特性。
缺点:
在创建子类时会调用两次超类的构造函数。
4 原型式继承:更像是拷贝,借助原型可以基于已有的对象创建新对象。
let person = {
name:"haha",
family:['papa','mama']
}
let child = Object.create(person);
ECMAScript5通过新增Object.create()方法规范化了原型式继承,这个方法接收两个参数:一个用作新对象原型的对象和为新对象定义属性的对象
Object.create()手写。先创建了一个空的临时性构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。本质上object()就是完成了一次浅复制操作:
function create() {
function f(){}
f.prototype = obj;
return new f();
}
缺点:
父类引用属性会被所有实例共享,因为是用整个父类对象来充当了子类原型对象。
无法传递参数。
5 寄生式继承:与原型式继承紧密相关的一种思路。
创建一个仅用于封装继承函数过程的函数,该函数在内部以某种方式来增强对象,最后返回对象。
let parent = {
name:"haha",
family:['papa','mama']
}
function createChild(parent) {
let child = Object.create(parent);
// 增强的方法
child.functionXX = function(){xx}
return child;
}
let child = createChild(parent);
缺点:和上面原型式继承一样。(就是给原型式继承套上一层函数而已,让原型式看起开更像继承,并没有解决根本问题)
6 寄生式组合继承
以上的方式多少都会有点缺陷,要达到完美继承就需要在组合式继承(伪经典继承)身上进行改造,但是要达到能传参,并且还要实现多继承的目的,那么在子类内部调用父类构造函数Father.call()这一步就不能动。
只能考虑Child.prototype = new Father 这一步,最终需要达到目的:Child.Prototype.__ proto __ = Father.prototype(子类的原型指向父类的原型, 解决父类调用两次的缺陷)----->(也就是一个对象继承另一个对象,上面的寄生式和原型式继承方式),另外最后还是需要将子类实例的constructor指回子类。
//1. 父类 实例属性放在构造函数中
function Parent(name, age) {
this.name = name
this.age = age
this.hobby = ['a', 'b', 'c']
}
// 父类方法放在原型上实现复用
Parent.prototype.sayName = function () {
console.log(this.name)
}
//2. 子类
function Child(name, age) {
Parent.call(this, name, age) // 调用父类的构造函数 (继承父类的属性)
this.a = 1
}
// 创建父类的原型副本,改变子类的原型,同时纠正构造函数
function inherit(subClass, superClass) {
var parent = Object.create(superClass.prototype);
parent.constructor = subClass;
subClass.prototype = parent;
}
inherit(Child, Parent);
完美实现了函数复用,传参,实例之间不会相互影响,多继承
参考:
https://juejin.cn/post/6844904161071333384
https://juejin.cn/post/7019185008527015949
https://juejin.cn/post/7080795936439402504