# ES6-Class

ES6 的class可以看作只是一个语法糖,注意以下几个可能会犯错误的点。

# 1. 实例属性和原型方法

实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层(等于号赋值)

类中的方法是定义在原型链上(Animal.prototyp),包括getter/setter函数。

详细可看Babel Class解析为ES5的代码 (opens new window)

class Animal {
  // 实例属性,定义在类实例对象上(a.speak1)
  name = 'MyAnimal'
  speak1 = () => {
    return this
  }

  // 原型链方法,定义在原型上(Animal.prototype.speak)
  speak() {
    return this;
  }
  // get也是定义在原型上(Animal.prototype.[get getName])
  get getName() {
    return this.name
  }
}

const a = new Animal()
console.log(a) // { name: 'MyAnimal', speak1: f , __proto__: {constructor, speak, ...} }

# 2. this 指向

实例属性this在定义时就已确定指向当前实例(箭头函数)。

类的方法(包括get/set函数)this默认指向当前实例,但一旦遇到解构时(上下文环境变化),this的值可能变化。

class Animal {
  // 实例属性。this在书写代码时,就已经确定为当前实例对象(箭头函数)
  speak1 = () => {
    console.log(this, 'speak1') // 在代码
    return this
  }

  // 原型链方法。this依赖于运行环境,默认是当前实例对象
  speak() {
    console.log(this, 'speak')
    return this;
  }
}

let a = new Animal()
a.speak() // a
a.speak1() // a

let { speak, speak1 } = a
speak() // undefined。重要:解构后原型链上的方法,this为undefined
speak1() // a

# 3. 继承时的原型链

类继承时,属性是直接继承,而原型链上的方法是一层一层的继承。

class B extends Animal {
    b1 = () => {
        console.log(this)
    }
    b() {
        console.log(this)
    }
}

// B(属性)->B.prototype -> A.prototype
console.log(new B()) // { speak1, b1, __proto__(B): {b, __proto__(A): {speak} } }

class C extends B {
    c1 = () => {
        console.log(this)
    }
    c() {
        console.log(this)
    }
}
// C(属性) -> C.prototype ->B.prototype -> A.prototype
console.log(new C()) // { speak1, b1, c1, __proto__(C): {c, __proto__(B): {b, __proto__(A):{speak} } } }

# 4. ES5和ES6继承的不同

ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。

ES6的继承则不同,在单重继承时,基本同ES5一致;当多重继承时,子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。详细看以下babel es6转es5 _possibleConstructorReturn应用。

es6:

class Person {
  static body = 'bofy';
  constructor(age) {
    this.name = 123
    this.age = age
  }

  getName() {
    return this.name
  }
}

class Doctor extends Person {
    constructor(age) {
      super(age)
      this.skill = 'shoushu'
    }
}

// 多重继承
class Superman extends Doctor {
}

var d = new Doctor(35)
d.name

babel 转译为 es5:

"use strict";

function _typeof(obj) {
  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
    _typeof = function _typeof(obj) {
      return typeof obj;
    };
  } else {
    _typeof = function _typeof(obj) {
      return obj &&
        typeof Symbol === "function" &&
        obj.constructor === Symbol &&
        obj !== Symbol.prototype
        ? "symbol"
        : typeof obj;
    };
  }
  return _typeof(obj);
}

function _possibleConstructorReturn(self, call) {
  // call有值,则返回call
  if (call && (_typeof(call) === "object" || typeof call === "function")) {
    return call;
  }
  return self
}

function _getPrototypeOf(o) {
  // setPrototypeOf是ES6提案
  _getPrototypeOf = Object.setPrototypeOf
    ? Object.getPrototypeOf
    : function _getPrototypeOf(o) {
        // getPrototypeOf在ES5就有,只是ES6中有一些改动
        return o.__proto__ || Object.getPrototypeOf(o);
      };
  return _getPrototypeOf(o);
}

// 原型链继承
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 }
  });
  // 设置subClass.__proto__ = superClass,为的是getPrototypOf时能拿到superClass这个构造函数。
  if (superClass) _setPrototypeOf(subClass, superClass);
}

function _setPrototypeOf(o, p) {
  _setPrototypeOf =
    Object.setPrototypeOf ||
    function _setPrototypeOf(o, p) {
      o.__proto__ = p;
      return o;
    };
  return _setPrototypeOf(o, p);
}

function _instanceof(left, right) {
  if (
    right != null &&
    typeof Symbol !== "undefined" &&
    right[Symbol.hasInstance]
  ) {
    return right[Symbol.hasInstance](left);
  } else {
    return left instanceof right;
  }
}

// 只允许new方式创建class
function _classCallCheck(instance, Constructor) {
  if (!_instanceof(instance, Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

function _defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
    var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    Object.defineProperty(target, descriptor.key, descriptor);
  }
}

function _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}

function _defineProperty(obj, key, value) {
  if (key in obj) {
    Object.defineProperty(obj, key, {
      value: value,
      enumerable: true,
      configurable: true,
      writable: true
    });
  } else {
    obj[key] = value;
  }
  return obj;
}

var Person =
  /*#__PURE__*/
  (function() {
    function Person(age) {
      // 检查必须使用new关键字创建类实例
      _classCallCheck(this, Person);

      this.name = 123;
      this.age = age;
    }

    // class中的方法,都是在Person.prototype
    // class中的静态属性,都是在Person上
    _createClass(Person, [
      {
        key: "getName",
        value: function getName() {
          return this.name;
        }
      }
    ]);

    return Person;
  })();

_defineProperty(Person, "body", "bofy");

var Doctor =
  /*#__PURE__*/
  (function(_Person) {
    // Doctor继承自Person
    // Doctor.prototype.__proto__ === Person.prototype(因为Doctor.prototype = Object.create(Person.prototype))
    // Doctor.__proto__ === Person(因为Object.setPrototypeOf(Doctor, Person))
    _inherits(Doctor, _Person);

    function Doctor() {
      var _this;

      _classCallCheck(this, Doctor);

      /**
       * 子类必须在constructor方法中调用super方法,否则新建实例时会报错。
       * 等同于
       * _this = Person.call(this, age) ?Person.call(this, age)   : this
       * 在单重继承时,Person.call()执行后返回空;多重继承时, Person.call()有值返回
      * **/
      _this = _possibleConstructorReturn(
        this,
        _getPrototypeOf(Doctor).call(this, age)
      );

      _this.skill = "shoushu";
      // 返回this。在多重继承中,this是父类的this(即:子类实例的构建,基于父类实例。)
      return _this;
    }

    return Doctor;
  })(Person);

var Superman =
/*#__PURE__*/
function (_Doctor) {
  _inherits(Superman, _Doctor);

  function Superman() {
    _classCallCheck(this, Superman);

    /**
      * class有多重继承特征,此时子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。
    **/
    return _possibleConstructorReturn(this, _getPrototypeOf(Superman).apply(this, arguments));
  }

  return Superman;
}(Doctor);

var d = new Doctor(35);
d.name;