ES6实践

在一个新项目上使用 ES6 语法有一段时间了,自己在学习 React 时,也尝试了不少 ES6 的语法。这里记录一下学习实践的经验。

Compiler

使用 Babel 进行编译。
实际使用中遇到的问题:

  • 由于需要兼容 IE8,不支持 Object.defineProperty,使用 Babel 时,需要使用关闭 es6.module 选项
  • Babel 严格遵守 ES2015 模块标准,ES2015 模块默认是在严格模式下,所以最外层的 this 并不是指的 window 对象,在使用其他第三方库时遇到过这个问题,在编译时,需要加入 blacklist: ["strict"] 选项。

Main Syntax

这里说明了 ES6 的主要语法,实际根据实用程度以及对 ES6 语法的理解程度,并不会全部用到,这里介绍目前用到的语法,后面用到并理解后一点一点补充。

Arrows

箭头函数是很常用的语法了,基本抛弃了 function 关键字,更加简洁。
arrow functions 带来最大的改变就是固定了 this 的对象,箭头函数中的 this 指向包含该箭头函数的代码的 this 值,在使用过程中需要特别注意 this 的使用。
对比以下两段代码。

1
function Person() {
  var self = this;
  self.age = 0;
  setInterval(function growUp() {
    self.age++;
  }, 1000);
}

setInterval 中 this 一直指向 window 对象,在 Person 中缓存 this 值。

1
function Person(){
  this.age = 0;
  setInterval(() => {
    this.age++;
  }, 1000);
}

使用箭头函数后,this 指向其外层的 this 值。前者既是后者使用 bable 编译的结果
Nicholas 有篇介绍 arrow functions 中 this文章

Class

Class 语法,除了在用 React 时,自己到用的不多,这里是详细的语法介绍,在使用前需要好好看一下这个文档,后续有较多使用经验时在补充吧,这里不介绍了。
这里学习下,其编译后的代码原理。
首先可以看到其通过 _classCallCheck 来判断是否通过 new 关键字来创建对象,直接调用 Point() 会报错。

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

然后调用 _createClass 方法来绑定属性和方法。原型方法绑定到 Constructor.prototype(Point.prototype) 上,静态方法绑定到 Constructor(Point) 上。在对象上定义属性时,使用了 Object.defineProperty() 方法,这里是其详细的说明。

1
function (Constructor, protoProps, staticProps) {
        if (protoProps) defineProperties(Constructor.prototype, protoProps);
        if (staticProps) defineProperties(Constructor, staticProps);
        return Constructor;
    };

当有类继承时,如例子中的 ColorPoint 继承 Point 类时,在子类只增加了一些处理。
_inherits 方法将 ColorPoint.prototype 指向 由Point 的 prototype 上新创建的对象,并设置 ‘constructor’。

1
function _inherits(subClass, superClass) {
    if (typeof superClass !== 'function' && superClass !== null) {
        throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass);
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });
    if (superClass) subClass.__proto__ = superClass;
}

继续看 下面的代码

1
_get(Object.getPrototypeOf(ColorPoint.prototype), 'constructor', this).call(this, x, y);

Object.getPrototypeOf(ColorPoint.prototype) 得到的是 ColorPoint.prototype.__proto__Point.prototype.
_get() 函数,返回父类 Point ,并在 ColorPoint 的 this 上,向 Point 传入 x, y的值,进行子类的初始化。

Enhanced Object Literals

增强的对象字面量,可以继续看官方的例子
这里是主要通过 Object.defineProperty 来实现属性的设置,Loose mode 下,直接给对象设置属性

1
function _defineProperty(obj, key, value) {
    return Object.defineProperty(obj, key, {value: value, enumerable: true, configurable: true, writable: true});
}

// lose mode
_obj2.__proto__ = theProtoObj;
_obj2.handler = handler;
...

这里有以下几个用法:

同名变量简写

1
var obj = {
    handler
}
相当于
var handler = xxx;
var obj = {
    handler: handler
}

方法简写

省略了 function 关键字

1
var handler = xxx;
var obj = {
    toString() {
    return "d " + super.toString();
    }
}
// 相当于
var obj = {
    toString: function toString() {
        return "d " + _get(Object.getPrototypeOf(_obj), "toString", this).call(this);
    }
}

_get(Object.getPrototypeOf(_obj), "toString", this).call(this) 即调用了 Object 的 toString方法。

动态属性名

1
var obj = {
    [ 'prop_' + (() => 42)() ]: 42
};

Template Strings

很好用的一个语法,主要用于多行字符串和字符串变量替换

1
`In JavaScript this is
 not legal.`

var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

Destructuring

解构用的不多,还是推荐看一下 用 bable 后的代码,加深理解。

Default + Rest + Spread

函数参数具有默认值,剩余参数以及参数展开功能,使用方法和原理也比较简单。

Let + Const

ES6 加入的很常用的关键字,代替了 var 的使用。
let 表示块作用域变量,const 表示常量。
观察其编译后的代码,可以发现其还是用 var 实现,Babel 会在编译时去检查语法的使用(相同块作用域不能重复使用 let 声明,常量不可以在赋值等等)

Modules

目前使用该语法,主要通过 Broswerify 或者 Webpack 实现模块的 bundle,不过异步加载模块并没有相应的语法 ,平时最常用也是推荐用的是 Default exports,其完整语法比较复杂,如果要用的话,推荐深入看一下文档

其他语法都不是很常用,等实际中有使用经验后在总结。
最后还有一个非常重要而且非常有用的语法 Promise,这个后面单独介绍。

相关资源:
使用ES6进行开发的思考:http://efe.baidu.com/blog/es6-develop-overview/