ES6学习——class到底是个啥

1 篇文章 0 订阅

引言:

在重新看红宝书的时候。复习到面向对象的程序设计这一章节,重温了一下对象,原型,属性这三个核心概念和用法。顺便想到,在ES6中,可以通过class这一个关键字,用一种清爽的方式来定义一个类(实际上是一个对象,但是在语义上当做一个class)。所以实际上,class实现了构造一个对象的语法糖。


ES5是怎么样的?

在es5的时候,定义一个对象的方法是:
function Circle(radius) {
    this.radius = radius;
    Circle.circlesMade++;
}

Circle.draw = function draw(circle, canvas) { /* Canvas drawing code */ }

Object.defineProperty(Circle, "circlesMade", {
    get: function() {
        return !this._count ? 0 : this._count;
    },

    set: function(val) {
        this._count = val;
    }
});

Circle.prototype = {
    area: function area() {
        return Math.pow(this.radius, 2) * Math.PI;
    }
};

Object.defineProperty(Circle.prototype, "radius", {
    get: function() {
        return this._radius;
    },

    set: function(radius) {
        if (!Number.isInteger(radius))
            throw new Error("Circle radius must be an integer.");
        this._radius = radius;
    }
});
  值得关注的点有:
1. 对象的私有属性和方法,在constructor内部通过this绑定。
2. 对象的公有方法和属性,通过prototype来绑定。
3. 对象的属性的getter和setter,如果需要定制,需要通过defineObject()的方式来定义。
  明显这种写法对于标准的面向对象方式来讲,是很不友好的。因此ES6中class的定义,本质上是一种语法糖,将对象的定义,用一种“类”的方式写出来。但是本质上,还是实现了ES5的语法。

ES6该怎么写呢?

针对上面的类,如果按照ES6使用class来写,则有以下的写法:
class Circle {
    constructor(radius) {
        this.radius = radius;
        Circle.circlesMade++;
    };

    static draw(circle, canvas) {
        // Canvas drawing code
    };

    static get circlesMade() {
        return !this._count ? 0 : this._count;
    };
    static set circlesMade(val) {
        this._count = val;
    };

    area() {
        return Math.pow(this.radius, 2) * Math.PI;
    };

    get radius() {
        return this._radius;
    };
    set radius(radius) {
        if (!Number.isInteger(radius))
            throw new Error("Circle radius must be an integer.");
        this._radius = radius;
    };
}

从以上的定义中,可以看到,对于一个class的定义:
1. 私有属性还是通过constructor进行传递
· 2. 公共的方法和属性,在constructor的外部定义,同时省略关键字function,该方法对应prototype上的方法,所有的实例共享一个Prototype 对象
*3. static方法表明,该方法是定义在类上的。直接通过类来调用,而不能通过实例对象来调用。这个方法在原来的es5中并不存在,属于新的特性。
4.getter和setter的定义可以当做一个特殊的方法,在属性前面声明get /set,然后定义自定义的getter和setter。

ES5 和 ES6的转换

使用babel,将以上es5的代码经过编译,结果如下:
"use strict";

var _createClass = 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Circle = function () {
  function Circle(radius) {
    _classCallCheck(this, Circle);

    this.radius = radius;
    Circle.circlesMade++;
  }

  _createClass(Circle, [{
    key: "area",
    value: function area() {
      return Math.pow(this.radius, 2) * Math.PI;
    }
  }, {
    key: "radius",
    get: function get() {
      return this._radius;
    },
    set: function set(val) {
      if (!Number.isInteger(val)) throw Error("not a nubmer");
      this._radius = val;
    }
  }], [{
    key: "circlesMade",
 [{
    key: "area",
    value: function area() {
      return Math.pow(this.radius, 2) * Math.PI;
    }
  }, {
    key: "radius",
    get: function get() {
      return this._radius;
    },
    set: function set(val) {
      if (!Number.isInteger(val)) throw Error("not a nubmer");
      this._radius = val;
    }
  }], [{
    key: "circlesMade",
    get: function get() {
      return !this._count ? 0 : this._count;
    },
    set: function set(val) {
      this._count = val;
    }
  }]

get: function get() { return !this._count ? 0 : this._count; }, set: function set(val) { this._count = val; } }]); return Circle;}();

让我们看看babel做了那些事情。根据上文的分析,可以发现定义的属性有三类,1是私有的属性,2是公有的属性和方法,3是静态的属性和方法。babel首先遍历class,将这三类三种属性分别筛选出来,生成配置项。
私有的属性直接在function的构造器内部定义。公有的方法,将value设置为function的定义,有自定义getter和setter的属性,将get和set放在属性的配置项里面。这两类将成为成为公有的属性和方法,放在protoType里面。最后一类是静态属性和方法,定义在staticProps里面。将所有的定义和方法区分并且相应的config,最后形成的格式为:

 [{ // protoProps,最终将define到Constructor.prototype里面
    key: "area",
    value: function area() {
      return Math.pow(this.radius, 2) * Math.PI;
    }
  }, {
    key: "radius",
    get: function get() {
      return this._radius;
    },
    set: function set(val) {
      if (!Number.isInteger(val)) throw Error("not a nubmer");
      this._radius = val;
    }
  }], [{ // staticProps,define到Constructor上
    key: "circlesMade",
    get: function get() {
      return !this._count ? 0 : this._count;
    },
    set: function set(val) {
      this._count = val;
    }
  }]
babel定义了一个包装器,接受这些配置项,然后将这些属性根据config,定义在相应的位置上。这个包装器的定义如下:
var _createClass = function () { 
  function defineProperties(target, props) { 
    for (var i = 0; i < props.length; i++) { // 获取所有属性
      var descriptor = props[i]; 
      descriptor.enumerable = descriptor.enumerable || false; // 添加enumberable属性
      descriptor.configurable = true; // 添加configurable属性
      if ("value" in descriptor) descriptor.writable = true;  //添加writable属性
      Object.defineProperty(target, descriptor.key, descriptor); } // 将属性定义在target上
    } 
      return function (Constructor, protoProps, staticProps) 
      { if (protoProps) 
          defineProperties(Constructor.prototype, protoProps); // 如果有protoProps,则定义在constructor.prototype上
        if (staticProps) 
          defineProperties(Constructor, staticProps); // 如果有staticProps,则定义在类本身身上。
        return Constructor; }; 
      }();

可以看到这个包装器做的事情就两件:
1,如何根据props,配置配置项,并且把根据defineObject,粘到Object上。
2,根据配置项的类别,选择是配置在protoType上,还是类本身身上,从而达到static的目的。
经过这样的变换之后,可以得到:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值