前言

es6的class其实是构造函数的语法糖,但是又有区别,下面来详细分析下class

定义

先来看下class的定义的代码

   class Point{
     constructor(){}
     toString(){}
   }
   var p = new Point()
   p.constructor === Point.prototype.contructor//true
   Object.keys(Point.prototype)//[]
   Object.getOwnPropertyNames(Point.prototype)//['constructor','toString']

区别

  1. 在类的实例上调用方法,就是调用类的原型上的方法,但是类内部的方法是不可枚举的,构造函数的方法是可枚举的(是否可被for...in遍历,object.keys),构造函数可以遍历除contructor之外的方法
  2. constructor是类默认的方法,通过new生成实例时自动调用该方法,如果没有被显示定义,这个方法会被自动创建。
  3. 类只能通过new生成实例对象,如果像直接调用class会报错。
  4. 类的所有实例共享一个原型对象
    p2.__proto__ === p1.__proto__;//true
    这也就意味着可以通过实例的__proto__属性为class添加方法,不推荐使用,因为会影响到其他实例
  5. class不存在变量提升,这个和继承有关,必须保证子类在父类之后定义
  6. 类和模块内部默认都是使用严格模式
  7. class可以自定义原生数据结构(Array,String等)的子类,这是es5无法做到的,因为es5是先新建子类的this,再将父类的属性添加到子类上,由于父类的内部属性子类无法获取,导致无法继承原生的构造函数

继承

子类必须在constructor中调用super方法,否则新建实例会报错,因为子类没有自己的this,而是继承了父类的this,这和es5中的继承不一样,
因为es5中的继承是先创造子类的实例对象this,再将父类的方法添加到this上(parent.call(this)),es6中是先继承父类的实例对象this,然后再用子类的构造函数修改this。如果子类没有定义constructor,那么这个方法会被默认添加。
在子类的构造函数中,只有调用super关键字之后,才可使用this关键字,否则会报错,因为子类实例的构建基于对父类实例的加工,只有super方法才能返回父类的实例。

prototype和__proto__
大多数浏览器的es5实现中,每一个对象都有__proto__属性(IE8除外),指向对应的构造函数的prototype属性。class作为构造函数的语法糖,同时有prototype属性和__proto__属性,因为存在两条继承链。

  1. 子类的__proto__属性表示构造函数的继承,总是指向父类
  2. 子类prototype属性的__proto__属性表示方法的继承,总是指向父类的prototype属性

class的静态方法

如果在一个方法前加上static关键字,就表示该方法不会被实例继承,而是直接通过类调用,成为‘静态方法’。
看下代码

class Foo{
  static classMethod(){}
}
Foo.classMethod();
class Bar extends Foo(){
   static classMethod(){
       return super.classMethod()
   }
}
Bar.classMethod();

上面Foo类有静态方法classMethod,只能通过Foo.classMethod调用,不可通过实例调用,否则会报错,父类的静态方法可被子类继承。

class的静态属性

静态属性是指class本身的属性,即class.propname,而不是定义在实例对象(this)上的属性。

class Foo{}
Foo.prop = 1;
Foo.prop //1

上面的方法可以读、写Foo类的静态属性prop,但是es6中规定,class中只有静态方法,没有静态属性

new.target属性

es6为new命令引入了new.target属性,返回new命令所作用的构造函数,如果构造函数不是通过new命令调用的,那么new.target会返回undefined,因此这个属性可以判断构造函数是如何被调用的


juan26
521 声望19 粉丝