美文网首页
ES6之Class

ES6之Class

作者: 六个周 | 来源:发表于2019-01-20 19:12 被阅读143次
    参考地址:http://es6.ruanyifeng.com/#docs/class

    本节缺少实战与深刻理解,本节内容目前理解云里雾里。

    一、Class的基本语法

    1.简介


    基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已.

    来个例子对比一下:
    ES6之前,生成实例对象的传统方法是通过构造函数。

    function Point(x, y) {
      this.x =x;
      this.y=y;
    }
    Point.prototype.toString = function() {
      return '('+this.x+','+this.y+')';
    }
    var p = new Point(1,2)
    

    上面的代码用ES6改写:

    class Point {
      constructor(x,y){
        this.x = x;
        this.y = y;
      }
    
      toString(){
         return '('+this.x+','+this.y+')';
      }
    }
    

    上面ES6改写的代码注意看两点:

    1.ES6写法定义了一个类,可以看到Point类里面有个constructor方法(构造方法)----ES5的构造函数Point,就对应的是ES6的Point类的构造方法
    2.Point类中我们还看到了toString方法。注意,定义的方法的时候,在方法前不要加function关键字,直接把函数定义放进去即可。还需要注意一点,方法之间是不能用逗号分隔的,否则会报错。

    ES6的类,完全可看作构造函数的另一种写法(在类的实例上面调用方法,其实就是调用原型上的方法),这两句话用代码说明:

    ##demo1
    class liumz(){}
    typeof liumz //"function"
    liumz === liumz.prototype.constructor //true
    
    ##demo2
    class B {}
    let b = new B();
    b.constructor === B.prototype.constructor // true
    

    构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。

    class Point {
      constructor() {
        // ...
      }
    
      toString() {
        // ...
      }
    
      toValue() {
        // ...
      }
    }
    
    // 等同于
    
    Point.prototype = {
      constructor() {},
      toString() {},
      toValue() {},
    };
    

    由于类的方法都定义在prototype对象上,所以类的新方法可以添加在protype对象上面:

    class Point {
      constructor(){
        // ...
      }
    Object.assign(Point.protype,{
      toValue(){},
      toString(){}
      })
    }
    

    现在看到上面一堆的代码与定义文字说明,不妨小憩一下,回顾上面到底说了个啥:

    ES6新引进了Class这个概念,这个概念的引入只是让对象原型的写法更清晰(Class的绝大部分功能在ES5中都可以实现),ES5中的构造函数相当于Class中的构造方法。类的数据类型就是函数,类本身指向构造函数。
    构造函数是有prototype属性的,这个属性在ES6的类上是继续存在的,只是换了一种写法.

    在充分理解了上面的内容时,我们继续深入,ES6的类,内部定义的方法,都是不可枚举的,啥意思?看代码:

    class Point{
      constructor(x,y){}
      toString(){}
    }
    Object.keys(Point.prototype) //[]
    Object.getOwnPropertyNames(Point.prototype)//["constructor","toString"]
    

    2.静态方法


    类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

    class Foo {
      static classMethod() {
        return 'hello';
      }
    }
    
    Foo.classMethod() // 'hello'
    
    var foo = new Foo();
    foo.classMethod()
    // TypeError: foo.classMethod is not a function
    

    注意,如果静态方法包含this关键字,这个this指的是类,而不是实例

    class Foo {
      static bar() {
        this.baz();
      }
      static baz() {
        console.log('hello');
      }
      baz() {
        console.log('world');
      }
    }
    
    Foo.bar() // hello
    

    虽然既然类的静态方法不可被实例所继承,但是却可以被子类继承,不赘述。

    3.实例属性的新写法


    实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。写法对比:

    //实例属性this._count定义在constructor()方法里面
    class IncreasingCounter {
      constructor() {
        this._count = 0;
      }
      get value() {
        console.log('Getting the current value!');
        return this._count;
      }
      increment() {
        this._count++;
      }
    }
    
    //属性定义在类的最顶层,其它不变
    class IncreasingCounter {
      _count = 0;
      get value() {
        console.log('Getting the current value!');
        return this._count;
      }
      increment() {
        this._count++;
      }
    }
    

    这种新写法的好处是,所有实例对象自身的属性都定义在类的头部,看上去比较整齐,一眼就能看出这个类有哪些实例属性。

    4.静态属性


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

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

    目前,只有这种写法可行,因为 ES6 明确规定,Class 内部只有静态方法,没有静态属性。现在有一个提案提供了类的静态属性,写法是在实例属性法的前面,加上static关键字。

    // 老写法
    class Foo {
      // ...
    }
    Foo.prop = 1;
    
    // 新写法
    class Foo {
      static prop = 1;
    }
    

    二、Class的继承

    1.简介


    Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

    1️⃣、子类必须在constructor方法中调用super方法,否则新建实例时会报错。

    class ColorPoint extends Point {
      constructor(x, y, color) {
        super(x, y); // 调用父类的constructor(x, y)
        this.color = color;
      }
    
      toString() {
        return this.color + ' ' + super.toString(); // 调用父类的toString()
      }
    }
    

    这是因为子类自己的this对象,必须通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后对其加工,加上子类自己的实例属性和方法,如果不调用super方法,子类就得不到this对象。
    2️⃣、父类的静态方法,也会被子类继承

    class A {
      static hello() {
        console.log('hello world');
      }
    }
    
    class B extends A {
    }
    
    B.hello()  // hello world
    

    上面代码中,hello()是A类的静态方法,B继承A,业绩承了A的静态方法。

    2.Object.propotypeOf()


    Object.propotypeOf方法可以用来从子类上获取父类。

    Object.propotypeOf(colorPoint) === Point
    //true
    

    因此,使用这个方法判断,一个类是否继承了另一个类,

    3.super关键字


    super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同.

    第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。

    第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

    4.类的prototype属性和_proto_属性


    大多数浏览器的ES5实现中,每一个对象都有一个_prop_属性,指向相应的构造函数的prototype属性。
    Class作为构造函数的语法糖,同时有prototype属性和_prop_属性,因此同时存在两条继承链。

    • 子类的_prop_属性,表示构造函数的继承,总是指向父类。
    • 子类prototype属性的_prop_属性,表示方法的继承,总是指向父类的prototype属性。
    class A {
    }
    
    class B extends A {
    }
    
    B.__proto__ === A // true
    B.prototype.__proto__ === A.prototype // true
    

    5.原生构造函数的继承(本小节懵逼)


    ……

    6.Mixin模式的实现(本小节懵逼)


    Mixin 指的是多个对象合成一个新的对象,新对象具有各个组成成员的接口。它的最简单实现如下

    const a = {
      a: 'a'
    };
    const b = {
      b: 'b'
    };
    const c = {...a, ...b}; // {a: 'a', b: 'b'}
    

    上面代码中,c对象是a对象和b对象的合成,具有两者的接口。

    下面是一个更完备的实现,将多个类的接口“混入”(mix in)另一个类。

    function mix(...mixins) {
      class Mix {}
    
      for (let mixin of mixins) {
        copyProperties(Mix.prototype, mixin); // 拷贝实例属性
        copyProperties(Mix.prototype, Reflect.getPrototypeOf(mixin)); // 拷贝原型属性
      }
    
      return Mix;
    }
    
    function copyProperties(target, source) {
      for (let key of Reflect.ownKeys(source)) {
        if ( key !== "constructor"
          && key !== "prototype"
          && key !== "name"
        ) {
          let desc = Object.getOwnPropertyDescriptor(source, key);
          Object.defineProperty(target, key, desc);
        }
      }
    }
    

    上面代码的mix函数,可以将多个对象合成为一个类。使用的时候,只要继承这个类即可。

    class DistributedEdit extends mix(Loggable, Serializable) {
      // ...
    }
    

    相关文章

      网友评论

          本文标题:ES6之Class

          本文链接:https://www.haomeiwen.com/subject/pehxjqtx.html