es6之class\extends\super

253 阅读4分钟

ECMAScript 6 入门

一、class原理

(1)完全可以看作构造函数的另一种写法,定义一个类,实质就是定义一个构造函数。类的数据类型就是函数,类本身就指向构造函数。

class Point {
  // ...
}
typeof Point // "function"
Point === Point.prototype.constructor // true

(2)类调用必须使用new关键字,它跟普通构造函数的一个主要区别

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}
Point() // 报错 (构造函数不能没有new)
new Point() // 正确写的
ncaught TypeError: Class constructor Point cannot be invoked without 'new'

(3)es6 在线转译 www.babeljs.cn/repl/

上面es6 的代码转译之后可以:

var Point = function () {
  function Point(x, y) {
    _classCallCheck(this, Point);
    this.x = x;
    this.y = y;
  }

  _createClass(Point, [{
    key: "toString",
    value: function toString() {
      return '(' + this.x + ', ' + this.y + ')';
    }
  }]);
  return Point;
}();

function _classCallCheck(instance, Constructor) { 
  if (!_instanceof(instance, Constructor)) { 
    throw new TypeError("Cannot call a class as a function"); 
  } 
}
function _instanceof(left, right) { 
 if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { 
   return !!right[Symbol.hasInstance](left); 
 }  else { 
  return left instanceof right;  // 可以简单的直接看这里,检测对象是否是new出来的
 } 
}

_instanceof(instance, Constructor) 很明显的看出:检测对象是否是new出来的。
所以Point() // 报错 (构造函数不能没有new)

(4)类的内部所有定义的方法,都是不可枚举的,但是ES5 的写法,原型上定义的方法就是可枚举的。

class Point {
  constructor(x, y) {
    // ...
  }
  toString() {
    // ...
  }
}

Object.keys(Point.prototype) // []
Object.getOwnPropertyNames(Point.prototype) // ["constructor","toString"]

(5)其他知识点位

constructor方法默认返回实例对象(即this),但是完全可以指定返回另外一个对象。

实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。

类的所有实例共享一个原型对象,实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变“类”的原始定义,影响到所有实例。

参考class原理解析

二、exteds\super干了什么

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

ES6 的继承机制完全不同实质是:先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。子类实例的构建,基于父类实例,只有super方法才能调用父类实例。

class People{
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }
}
class lily extends People{
  constructor(){
     super()
    }
}

Babel转译之后一目了然:

(1)extends的过程中创建了一个自执行函数,并将父类传进去,继承父类之后再返回该子类

var lily = function (_People) {
  _inherits(lily, _People); // 继承父类原型
  function lily() { // 继承父类对象属性
    _classCallCheck(this, lily);
    return _possibleConstructorReturn(this, _getPrototypeOf(lily).call(this)); // super干的事
  }
  return lily;
}(People);

function _inherits(subClass, superClass) { 
    subClass.prototype = Object.create(superClass && superClass.prototype, { 
        constructor: { 
            value: subClass,
            writable: true, 
            configurable: true 
        } 
    }); 
   if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

(2)super() 的实质就是People.call(this) 

function _possibleConstructorReturn(self, call) { 
   //call指的是Object.getPrototypeOf(lily)).call(this),也就是People.call(this)
   return call && (typeof call === "object" || typeof call === "function") ? call : self;
}

在子类中如果调用constructor函数,必须调用super(),在子类的constructor函数实际上指向的是父类的constructor函数,因为在子类的constructor内部的this指向的是子类实例本身,需要super去改变this指向,可以调用super()方法传递参数,动态获得父类的继承。

super当做函数使用:super()

class A { constructor() {  console.log(new.target.name); //new.target指向当前正在执行的函数 }}class B extends A { constructor() {  super(); }}new A() // Anew B() // B

注意,super虽然代表了父类的构造函数,但是返回的是子类的实例,即super内部的this指的是B,因此super()在这里相当于A.prototype.constructor.call(this)。

可以看到,在super()执行时,它指向的是子类B的构造函数,而不是父类A的构造函数。也就是说,super()内部的this指向的是B。

super当作对象使用

在普通方法中

指向父类的原型对象;super.myMethod(msg)就相当Parent.prototype.myMethod(msg)

在静态方法中

指向父类; 注意:静态方法定义在类上,所以只能通过类来调用。

class Parent {
  static myMethod(msg) {
    console.log('static', msg);
  }
  myMethod(msg) {
    console.log('instance', msg);
  }
}

class Child extends Parent {
  static myMethod(msg) {
    super.myMethod(msg);
  }
  myMethod(msg) {
    super.myMethod(msg);
  }
}

Child.myMethod(1);
var child = new Child();
child.myMethod(2); 

参考exteds继承链接

三、static关键字定义静态方法

静态方法定义在类上,所以只能通过类来调用,同时子类可以继承父类的静态方法

class P {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  toString() {
    return '(' + this.name + ', ' + this.age + ')';
  }
  static F () {
	console.log('i am point')
  }
}
class C extends P {
	constructor(name, age){
		super('小小', 20)
		this.name = name
	}
}
var c1 = new C('hello', 20)

console.log(c1) // 输出实例
console.log(C.F) // 输入静态方法
console.log(c1.F) // undefined

小白菜,白了又了白。。。