南昌网络公司,南昌网站建设,南昌APP开发,南昌小程序开发,南昌网络推广,南昌网络营销,网络公司,网络营销,网站推广,网站优化,网站制作,网站设计,网站建设,百度SEO优化,小程序开发,公众号开发,APP开发,全网推广,网站制作,网页制作,高端网站设计,高端网站建设,南顺网络,南顺科技
当前位置 南顺网络>> 知识拓展

继承行为在 ES5 与 ES6 中的区别

ES5 中的继承


// Shape - 父类(superclass)

function Shape() {

  this.x = 0;

  this.y = 0;

}

// 父类的方法

Shape.prototype.move = function(x, y) {

  this.x += x;

  this.y += y;

  console.info('Shape moved.');

};

// Rectangle - 子类(subclass)

function Rectangle() {

  Shape.call(this); // call super constructor.

}

// 子类续承父类

Rectangle.prototype = Object.create(Shape.prototype);

Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

console.log('Is rect an instance of Rectangle?',

  rect instanceof Rectangle); // true

console.log('Is rect an instance of Shape?',

  rect instanceof Shape); // true

rect.move(1, 1); // Outputs, 'Shape moved.'

如上所示: 展示了一个 ES5 中实现单继承的例子,在《Javascript 高级程序设计》一书中,给这种继承方式定义为「寄生组合式继承」。不管什么形式,什么命名,在 ES5 中实现继承始终就是要坚持一个原则:将实例属性放在构造函数中挂在this上,将一些方法属性挂在原型对象上,子类可共享。 上面这种继承方式的关键在于两点:

子类构造函数通过 apply 或者 call 的方式运行父类的构造函数,此举将父类的实例属性挂在子类的 this 对象上

以父类的原型对象为基础,与子类的原型对象之间建立原型链关系,使用了 Object.create,本质在于 Child.prototype.__proto === Parent.prototype;


ES6 中的继承

class Point {

  constructor(x, y) {

    this.x = x;

    this.y = y;

  }

  toString() {

    return '(' + this.x + ', ' + this.y + ')';

  }

}

class ColorPoint extends Point {

  constructor(x, y, color) {

    super(x, y); // 调用父类的constructor(x, y)

    this.color = color;

  }

  toString() {

    return this.color + ' ' + super.toString(); 

  }

}

ES6 中的继承使用到了 extends 关键字,function 也变成了 class 关键字。class 的本质还是一个语法糖,这个大家都会脱口而出,但是在继承机制这里到底是如何做到的,我们看一下 babel 在此处是如何帮我们转译的,

var ColorPoint =

/*#__PURE__*/

function (_Point) {

  _inherits(ColorPoint, _Point);

  function ColorPoint(x, y, color) {

    var _this;

    _classCallCheck(this, ColorPoint);

    _this = _possibleConstructorReturn(this, _getPrototypeOf(ColorPoint).call(this, x, y)); // 调用父类的constructor(x, y)

    _this.color = color;

    return _this;

  }

  _createClass(ColorPoint, [{

    key: "toString",

    value: function toString() {

      return this.color + ' ' + _get(_getPrototypeOf(ColorPoint.prototype), "toString", this).call(this);

    }

  }]);

  return ColorPoint;

}(Point);

如上是经过babel转译后的代码,有几个关键点:

一、 _inherits()

function _inherits(subClass, superClass) {

    if (typeof superClass !== "function" && superClass !== null) {

        throw new TypeError("Super expression must either be null or a function");

    }

    subClass.prototype = Object.create(superClass && superClass.prototype, {

        constructor: {

            value: subClass,

            writable: true,

            configurable: true

        }

    });

    if (superClass) _setPrototypeOf(subClass, superClass);

}

首先完成extends对象的校验,必须是function 或者null,否则报错。其次完成以下事情:


ColorPoint.__proto__ === Point;

ColorPoint.prototype.__proto__ === Point.prototype;

二、 ColorPoint 构造函数中 _classCallCheck(), _possibleConstructorReturn()

function _classCallCheck(instance, Constructor) {

  if (!_instanceof(instance, Constructor)) {

      throw new TypeError("Cannot call a class as a function");

  }

}

主要是用来检测构造函数不能直接调用,必须是通过new的方式来调用。

function _possibleConstructorReturn(self, call) {

  if (call && (_typeof(call) === "object" || typeof call === "function")) {

      return call;

  }

  return _assertThisInitialized(self);

}

调用父类的构造函数,初始化一些实例属性,并将this返回。使用该返回的this赋值给子类的this对象,子类通过这一步返回的this对象,再该基础之上在添加一些实例属性。


这就是最大的不同之处。如果不经历这一步,子类没有this对象,一旦操作一个不存在的this对象就会报错。


三、 _createClass()

function _createClass(Constructor, protoProps, staticProps) {

  if (protoProps) _defineProperties(Constructor.prototype, protoProps);

  if (staticProps) _defineProperties(Constructor, staticProps);

  return Constructor;

}

最后一步完成原型属性与静态属性的挂载,如果是原型属性,挂在在Constructor上的prototype上,如果是静态属性或者静态方法,则挂在Constuctor 上。

来源:思否

原文链接:https://segmentfault.com/a/1190000021386998