ECMAScript6已经于近日进入了RC阶段,而早在其处于社区讨论时,我就开始一直在尝试使用ES6进行开发的方案。在Babel推出后,基于ES6的开发也有了具体可执行的解决方案,无论是Build还是Debug都能得到很好的支持。

而在有了充足的环境、工具之后,我们面临的是对ES6众多新特性的选择和分析,以便选取一个最佳的子集,让我们可以享受ES6带来的便利(减少代码量、提高可读性等)的同时,也可以顺利运行于当前以ES3-ES5为主的浏览器环境中。

经过分析后,本文试图对ES6各个特性得出是否适合应用的初步结论,并一一解释其使用场景。ES6的特性列表选自es6features

  • ★★★ 推荐使用
  • ★★ 有考虑地使用
  • ★ 慎重地使用
  • ☆ 不使用
特性 推荐程度
arrows ★★★
classes ★★★
enhanced object literals ★★★
template strings ★★★
destructuring ★★
default + rest + spread ★★★
let + const ★★★
iterators + for..of ★★
generators
unicode
modules ★★
module loaders
map + set + weakmap + weakset ★★
proxies
symbols
subclassable built-ins
promises ★★★
math + number + string + array + object APIs ★★★
binary and octal literals
reflect api
tail calls ★★

接下来我们以上特性挨个进行介绍。需要关注一点:如果你不想使用shim库(如Babel的browser-polyfill.jsgeneratorsRuntime.js)或者想使用尽可能少的helper(Babel的externalHelpers配置),那么需要按你的需求进一步缩减可使用的ES6特性,如MapSet这些就不应该使用。

语法增强类

Arrow function

Arrow functions是ES6在语法上提供的一个很好的特性,其特点有:

  • 语法更为简洁了。
  • 文法上的固定this对象。

我们鼓励在可用的场景下使用Arrow functions,并以此代替原有的function关键字。

当然Arrow functions并不是全能的,在一些特别的场景下并不十分适用,最为典型的是Arrow functions无法提供函数名称,因此做递归并不方便。虽然可以使用Y combinator来实现函数式的递归,但其可读性会有比较大的损失。

配合后文会提到的对象字面量增强,现在我们定义方法/函数会有多种方式,建议执行以下规范:

  • 所有的Arrow functions的参数均使用括号()包裹,即便只有一个参数:

    // Good
    let foo = (x) => x + 1; // Bad
    let foo = x => x + 1;
  • 定义函数尽量使用Arrow functions,而不是function关键字:

    // Good
    let foo = () => {
    // code
    }; // Bad
    function foo() {
    // code
    } // Bad
    let foo = function () {
    // code
    }

    除非当前场景不合适使用Arrow functions,如函数表达式需要自递归、需要运行时可变的this对象等。

  • 对于对象、类中的方法,使用增强的对象字面量:

    // Good
    let foo = {
    bar() {
    // code
    }
    }; // Bad
    let foo = {
    bar: () => {
    // code
    }
    }; // Bad
    let foo = {
    bar: function () {
    // code
    }
    };

增强的对象字面量

对象字面量的增强主要体现在3个方面:

可在对象中直接定义方法

let foo = {
bar() {
// code
}
};

我们推荐使用这种方式定义方法。

可使用通过计算得出的键值

let MY_KEY = 'bar';
let foo = {
[MY_KEY + 'Hash']: 123
};

我们推荐在需要的时候使用计算得出的键值,以便在一个语句中完成整个对象的声明。

与当前Scope中同名变量的简写

let bar = 'bar';
let foo = {
bar // 相当于bar: bar
};

我们并不推荐这样的用法,这对可读性并没有什么帮助。

模板字符串

模板字符串的主要作用有2个:

多行字符串

let html =
`<div>
<p>Hello World</p>
</div>`

从上面的代码中可以看出,实际使用多行字符串时,对齐是个比较麻烦的事。如果let html这一行本身又有缩进,那么会让代码更为难受一些。

因此我们不推荐使用多行字符串,必要时还是可以使用数组和join('')配合,而生成HTML的场景我们应该尽量使用模板引擎。

字符串变量替换

let message = `Hello ${name}, it's ${time} now`;

这是一个非常方便的功能,我们鼓励使用。但需要注意这些变量并不会被HTML转义,所以在需要HTML转义的场景,还是乖乖使用模板引擎或者其它的模板函数。

解构

解构(原谅我没什么好的翻译)是个比较复杂的语法,比如:

let [foo, bar] = [1, 2];
let {id, name, children} = getTreeRoot();

还可以有更复杂的,具体可以参考MDN的文档

对于这样一个复杂且多变的语法,我们要有选择地使用,建议遵循以下原则:

  • 不要一次通过解构定义过多的变量,建议不要超过5个。
  • 谨慎在解构中使用“剩余”功能,即let [foo, bar, ...rest] = getValue()这种方式。
  • 不要在对象解构中使用过深层级,建议不要超过2层。

函数参数增强

ES6为函数参数提供了默认值、剩余参数等功能,同时在调用函数时允许将数组展开为参数,如:

var foo = (x = 1) => x + 1;
foo(); // 2 var extend = (source, ...args) => {
for (let target in args) {
for (let name in Object.keys(target) {
if (!source.hasOwnProperty(name) {
source[name] = target[name];
}
}
}
}; var extensions = [
{name: 'Zhang'},
{age: 17},
{work: 'hard'}
];
extend({}, ...extensions);

我们鼓励使用这些特性让函数的声明和调用变得更为简洁,但有一些细节需要注意:

  • 在使用默认参数时,如果参数默认值是固定且不会修改的,建议使用一个常量来作为默认值,避免每一次生成的开销。
  • 不要对arguments对象使用展开运算,这不是一个数组。

关键字类

let和const

这是2个用来定义变量的关键字,众所周知的,let表示块作用域的变量,而const表示常量。

需要注意的是,const仅表示这个变量不能被再将赋值,但并不表示变量是对象、数组时其内容不能改变。如果需要一个不能改变内容的对象、数组,使用Object.freeze方法定义一个真正的常量:

const DEFAULT_OPTIONS = Object.freeze({id: 0, name: 'unknown'});

不过如果你在程序中能控制不修改对象的话,这并不具备什么意义,Object.freeze是否会引起执行引擎的进一步优化也尚未得到证实。

我们推荐使用let全面替代var。同时建议仅在逻辑上是常量的情况下使用const,不要任何不会被二次赋值的场景均使用const

迭代器和for..of

迭代器是个好东西,至少我们可以很简单地遍历数组了:

for (let item in array) {
// code
}

但是迭代器本身存在一些细微的缺点:

  • 性能稍微差了一些,对于数组来说大致与Array.prototype.forEach相当,比不过原生的for循环。
  • 不能在循环体中得到索引i的值,因此如果需要索引则只能用原生的for循环。
  • 判断一个对象是否可迭代比较烦人,没有原生方法提供,需要自行使用typeof o[Symbol.iterator] === 'function'判断。

对于迭代器,我们鼓励使用并代替原生for循环,且推荐关注以下原则:

  • 对于仅一个语句的循环操作,建议使用forEach方法,配合Arrow functions可非常简单地在一行写下循环逻辑。
  • 对于多个语句的循环操作,建议使用for..of循环。
  • 对于循环的场景,需要注意非数组但可迭代的对象,如MapSet等,因此除arguments这类对象外,均建议直接判断是否可迭代,而不是length属性。

生成器

生成器(Generators)也是一个比较复杂的功能,具体可以参考MDN的文档

对于生成器,我的建议是非常谨慎地使用,理由如下:

  • 生成器不是用来写异步的,虽然他确实有这样一个效果,但这仅仅是一种Hack。异步在未来一定是属于asyncawait这两个关键字的,但太多人眼里生成器就是写异步用的,这会导致滥用。
  • 生成器经过Babel转换后生成的代码较多,同时还需要generatorsRuntime库的支持,成本较高。
  • 我们实际写应用的大部分场景下暂时用不到。

生成器最典型的应用可以参考C#的LINQ获取一些经验,将对一个数组的多次操作合并为一个循环是其最大的贡献。

模块和模块加载器

ES6终于在语言层面上定义了模块的语法,但这并不代表我们现在可以使用ES6的模块,因为实际在ES6定稿的时候,它把模块加载器的规范给移除了。因此我们现在有的仅仅是一个模块的importexport语法,但具体如“模块名如何对应到URL”、“如何异步/同步加载模块”、“如何按需加载模块”等这些均没有明确的定义。

因此,在模块这一块,我们的建议是使用标准语法书写模块,但使用AMD作为运行时模块解决方案,其特点有:

  • 保持使用importexport进行模块的引入和定义,可以安全地使用命名export和默认export
  • 在使用Babel转换时,配置modules: 'amd'转换为AMD的模块定义。
  • 假定模块的URL解析是AMD的标准,import对应的模块名均以AMD标准书写。
  • 不要依赖SystemJS这样的ES6模块加载器。

这虽然很可能导致真正模块加载器规范定型后,我们的import模块路径是不规范的。但出于ES6的模块不配合HTTP/2简直没法完的考虑,AMD一定很长一段时间内持续存在,我们的应用基本上都是等不到HTTP/2实际可用的日子的,所以无需担心。

类型增强类

Unicode支持

这个东西基本没什么影响,我们很少遇到这些情况且已经习惯了这些情况,所以可以认为这个特性不存在而继续开发。

Map和Set

两个非常有用的类型,但对不少开发者来说,会困惑于其跟普通对象的区别,毕竟我们已经拿普通对象当MapSet玩了这么多年了,也很少自己写一个类型出来。

对于此,我们的建议是:

  • 当你的元素或者键值有可能不是字符串时,无条件地使用MapSet
  • 有移除操作的需求时,使用MapSet
  • 当仅需要一个不可重复的集合时,使用Set优先于普通对象,而不要使用{foo: true}这样的对象。
  • 当需要遍历功能时,使用MapSet,因为其可以简单地使用for..of进行遍历。

因此,事实上仅有一种情况我们会使用普通的对象,即使用普通对象来表达一个仅有增量Map,且这个Map的键值是字符串。

另外,WeakMapWeakSet是没有办法模拟实现的,因此不要使用

Proxy

这不是一个可以模拟实现的功能,没法用,因此不要使用Proxy

Symbol

Symbol最简单的解释是“可用于键值的对象”,最大的用处可能就是用来定义一些私有属性了。

我们建议谨慎使用Symbol,如果你使用它来定义私有属性,那么请保持整个项目内是一致的,不要混用Symbol和闭包定义私有属性等手段。

可继承的内置类型

按照ES6的规范,内置类型如ArrayFunctionDate等都是可以继承且没有什么坑的。但是我们的代码要跑在ES3-5的环境下,显然这一特性是不能享受的。

Promise

这个真没什么好说的,即便不是ES6,我们也已经满地用着Promise了。

建议所有异步均使用Promise实现,以便在未来享受asyncawait关键字带来的便携性。

另外,虽然Babel可以转换asyncawait的代码,但不建议使用,因为转换出来的代码比较繁琐,且依赖于generatorsRuntime

各内置类型的方法增强

Array.fromString.prototype.repeat等,这些方法都可以通过shim库支持,因此放心使用即可。

二进制和八进制数字字面量

这个特性基本上是留给算法一族用的,因此我们的建议是除非数字本身在二/八进制下才有含义,否则不要使用

反射API

Reflect对象是ES6提供的反射对象,但其实没有什么方法是必要的。

其中的delete(name)has(name)方法相当于deletein运算符,而defineProperty等在Object上本身就有一套了,因此不建议使用该对象

尾递归

当作不存在就好了……

转:http://efe.baidu.com/blog/es6-develop-overview/

使用ES6进行开发的思考的更多相关文章

  1. ES6的开发环境搭建

    在搭建es6开发环境之前,先简单介绍一下es6. ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在2015年6月正式发布了.它的目标,是使得 Java ...

  2. 从无到有实现搭建vue+ElementUI+less+ES6的开发环境并进行简单的开发的项目

    项目简介:该项目是基于日常计算宿舍水电煤气费的需求写的,旨在从无到有实现搭建vue+ElementUI+less+ES6的开发环境并进行简单的开发,使用webpack进行代码的编译.压缩和打包,并疏通 ...

  3. 一.ES6的开发环境搭建

    前言: 现在的Chrome浏览器已经支持ES6了,但是有些低版本的浏览器还是不支持ES6的语法,这就需要我们把ES6的语法自动的转变成ES5的语法.Webpack是有自动编译转换能力的,除了Webpa ...

  4. 使用Vue2+webpack+Es6快速开发一个移动端项目,封装属于自己的jsonpAPI和手势响应式组件

    导语 最近看到不少使用vue制作的音乐播放器,挺好玩的,本来工作中也经常使用Vue,一起交流学习,好的话点个star哦 本项目特点如下 : 1. 原生js封装自己的跨域请求函数,支持promise调用 ...

  5. J2EE学习从菜鸟变大鸟之八 企业级项目开发的思考

    什么是企业级项目开发 "企业级项目".企业级项目开发,Java也是企业级项目开发,这个我们到处说.听,每天被我们挂在嘴边,可是到底什么项目才算是"企业级"?自己 ...

  6. 开始学习es6(一) 搭建个es6的开发环境

    1.开始学习es6 如果想在浏览器跑es6  需要给es6个环境 因为一直用vue-cli全家桶 这样虽然方便 但如果用es6需要跑起个vue全家桶 于是想到可以用gulp搭建个开发环境 首先需要1. ...

  7. 关于WEB前端开发的思考与感悟

    万事开头难. 当我想要认真写一篇文章向大家分享我对前端的认识与感悟的时候,突然就深刻的体会到了这句话确实太有道理了. 最近几年对于web前端的传闻很多,比如人才稀缺,简单易学,待遇丰厚,整体势头发展良 ...

  8. WEB前端开发的思考与感悟

    当我想要认真写一篇文章向大家分享我对前端的认识与感悟的时候,突然就深刻的体会到了这句话确实太有道理了. 最近几年对于web前端的传闻很多,比如人才稀缺,简单易学,待遇丰厚,整体势头发展良好等等.遇到过 ...

  9. C语言开发的思考

    维护过十万行代码的通信协议,自己从头开始开发过几万行的代码,步骤: 1.移植性.为移植性对数据类型做重新定义. 2.内存计数.不要直接使用malloc和free,而是给所有类型的内存申请定义类型,并计 ...

随机推荐

  1. flash里面调用js

    在flash里面直接调用js 用这个:ExternalInterface.call("test");  test是函数名

  2. PHP优化的总结

    今天看了下PHPBB的相关规范,觉得有很多值得学习之处. 以下就几点PHP的优化做下总结: 1.in_array的用法 避免在大的数组上使用 in_array(),同时避免在循环中对包含20个以上元素 ...

  3. 算法导论之python实现插入排序

    插入排序的花费时间 c*n2, c 是常数 伪代码 INSERTION-SORT(A) for i  to A.length key = A[j] //Insert A[j] into the sor ...

  4. HTML&CSS基础学习笔记1.6-html的文本操作标签

    文本也许是HTML里最常见的元素了,所以我们有必要对HTML的文本操作标签做下认识. 1. <em>,<i>内的文字呈现为倾斜效果: 2. <strong>,< ...

  5. C++得到最大的int值

    要得到最大的int值: 1.利用(unsigned int)-1,这样得到的就是unsigned int表示的最大值. 2.int值只是比unsigned int多一位符号位,所以对(unsigned ...

  6. OOP(面向对象编程)的一些特性

    接口:接口是把公共实例(非静态)方法和属性结合起来,以封装特定功能的一个集合.一旦定义了接口,就可以在类中实现它.接口注意事项:接口不能单独存在.不能像实例化一个类那样实例化接口.另外,接口不能包含实 ...

  7. Qt跨线程信号和槽的连接(默认方式是直连和队列的折中)

    Qt支持三种类型的信号-槽连接:1,直接连接,当signal发射时,slot立即调用.此slot在发射signal的那个线程中被执行(不一定是接收对象生存的那个线程)2,队列连接,当控制权回到对象属于 ...

  8. MyBatis里json型字段到Java类的映射

    一.简介 我们在用MyBatis里,很多时间有这样一个需求:bean里有个属性是非基本数据类型,在DB存储时我们想存的是json格式的字符串,从DB拿出来时想直接映射成目标类型,也即json格式的字符 ...

  9. Teach Yourself Scheme in Fixnum Days 13 Jump跳转

    Jumps One of the signal features of Scheme is its support for jumps or nonlocal control. Specificall ...

  10. win7系统如何恢复administrator用户

    默认情况下,administrator用户是禁用的. 要恢复的话,右键单击我的电脑 管理-->本地用户和组-->用户-->右键属性 把"账户已禁用"前的选择符号去 ...