ES3、ES5、ES6继承

转载自:
ES3、ES5、ES6继承
两张图理解继承
Mixin继承、高阶函数、装饰器

1.对象有属性 proto, 指向该对象的构造函数的原型对象。
2.方法除了有属性 proto, 还有属性prototype,prototype指向该方法的原型对象。
(类/方法本身的 proto 指向 js 方法本身的定义,无意义)

JS 中的继承有多种实现方式,可以分成下面四类:
  • Mixin 模式,即属性混入,从一个或多个对象中复制属性到新的对象中
  • 方法借用模式,即通过 call 或 apply 实现方法的重用
  • 原型模式,使用 Object.create 方法直接以一个对象为原型创造新的对象
  • 类模式,实际上是使用构造函数或 ES6 class

前三种有一个共同点,就是没有“类”的概念,它们在适当的场景下非常有用,不过也因为没有类,缺失了很多经典面向对象继承的要素。例如父子对象之间没有严格的传承关系,即不一定是 is-a 的关系,这决定了无法将它们直接应用在面向对象分析与设计方面,可以说它们并不是真正的继承,而是介于继承和组合之间的代码复用方案。
而第四种,类式继承,无论是使用构造函数还是 ES6 加入的 class,都能表达明确的继承关系,在需要对继承重度使用的场景下,应该使用类式继承。接下来,本文讨论的都是类式继承。

有一点需要牢记:继承是一种强耦合,应该谨慎使用。

用 ES3 实现继承

实现要点:

利用 Person.call(this) 执行“方法借用”,获取 Person 的属性
利用一个空函数将 Person.prototype 加入原型链

function Person(name) {
  this.name = name;
}

Person.prototype.printName = function() {
  console.log(this.name);
};

function Bob() {
  Person.call(this, "Bob");
  this.hobby = "Histroy";
}

function inheritProto(Parent, Child) {
  var Fn = function() {};
  Fn.prototype = Parent.prototype;
  Child.prototype = new Fn();
  Child.prototype.constructor = Child;
}

inheritProto(Person, Bob);

Bob.prototype.printHobby = function() {
  console.log(this.hobby);
};

console.dir(new Bob());
用 ES5 实现继承

实现要点:

利用 Person.call(this) 执行“方法借用”,获取 Person 的属性
利用 ES5 增加的 Object.create 方法将 Person.prototype 加入原型链

function Person(name) {
  this.name = name;
}

Person.prototype.printName = function() {
  console.log(this.name);
};

function Bob() {
  Person.call(this, "Bob");
  this.hobby = "Histroy";
}

Bob.prototype  = Object.create(Person.prototype, {
  constructor: {
    value: Bob,
    enumerable: false,
    configurable: true,
    writable: true
  }
});

Bob.prototype.printHobby = function() {
  console.log(this.hobby);
};

console.dir(new Bob());
用 ES6 实现继承

实现要点:
利用 ES6 增加的 class 和 extends 实现比以前更完善的继承

class Person {
  constructor(name) {
    this.name = name;
  }

  printName() {
    console.log(this.name);
  }
}

class Bob extends Person {
  constructor() {
    super("Bob");
    this.hobby = "Histroy";
  }

  printHobby() {
    console.log(this.hobby);
  }
}
console.dir(new Bob());
super

使用 super 来实现更简化、更灵活的多态方法
再来看 ES6 中的 super,子类的方法想借助父类的方法完成一部分工作时,super 就可以派上用场了,这是比继承更为细粒度的代码复用,不过耦合性也也变得更强了。实际上 super 也有很多功能,既可以当作函数使用,也可以当作对象使用。

class 继承中的箭头函数

第一步,我们使用普通创建类的方式声明了一个类 A

class A {
  files = []
  startUpload = () => {
    ...
  }
}

复制代码这是一个文件上传的简易类,此时我们又一个需求,就是在某种情况下需要实现在 startUpload 之前对 files 进行重新排序,为了避免强耦合,本来打算用组合或者注入高阶函数的方式实现,最后还是选择了继承。

class B extends A {
  sort () {
       ...
  }
  startUpload = () => {
    this.sort()
    super.startUpload()
  }
}

复制代码然而问题出现了,我们使用图中的方式根本无法实现真正意义上的拓展,问题就出在了箭头函数上:

第二步分析:箭头函数是被声明在了对应的 this 上,因此在子类上二次声明 startUpload 方法是对父类 this 上属性的覆盖,而非并存。而且在语法层面上 es6 根本不允许在箭头函数上访问关键字 super。因此解决方案就是将箭头函数全部替换为对应的普通方法,将方法放到类的原型上,这样就可以实现了方法的继承,而非覆盖。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值