IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Getting started with ECMAScript 6

    dwqs发表于 2015-09-01 01:44:21
    love 0

    1、尝试ES6

    这里有三种简单地方式用于ES6编程:
    1. Web浏览器:使用Babel REPL,可以将ES6编译成ES5的平台,并且并不需要安装。
    2. 命令行:使用babel-node,可以执行ES6的Node.js版本(会在内部编译es5)。需要通过npm安装。
    3. 各种js引擎:根据ES6语法兼容表,找出被支持的ES6功能。
    对于第一点和第二点,这有更多细节。

    1.1 Babel REPL

    Babel REPL主要有四个部分:
    1、左上角包含es6源代码
    2、左下角可以查看es6中的语法错误
    3、右上角是es6被编译成es5的源代码
    4、右下角是通过console.log()的输出结果

    1.2 babel-node

    babel-node可以通过npm安装:

    1. $ npm install --global babel

    跟node一样,可以命令开启REPL交互:

    1. $ babel-node

    一旦开启REPL,就可以去执行ES6代码:

    1. > let arr = [1, 2, 3];
    2. > arr.map(x => x * x)
    3. [ 1, 4, 9 ]

    注意:babel-node目前不支持多行输入
    Babel官网上有管多关于Babel CLI的工具。

    2. 从var到let/const

    es6有两种新的方式来声明变量:
    1、let用于声明块级作用于变量
    2、const用于声明常量,其值不能被改变。

    let或const可以用来替代var声明变量,但是不能盲目使用,因为不同的作用域会改变代码的行为。例如:

    1. var x = 3;
    2. function func(randomize) {
    3. if (randomize) {
    4. var x = Math.random(); // (A) scope: whole function
    5. return x;
    6. }
    7. return x; // accesses the x from line A
    8. }
    9. func(false); // undefined

    func()会意外地返回undefined。你可以重写这部分代码,就知道为什么返回undefined了:

    1. var x = 3;
    2. function func(randomize) {
    3. var x;
    4. if (randomize) {
    5. x = Math.random();
    6. return x;
    7. }
    8. return x;
    9. }
    10. func(false); // undefined

    如果用let代替之前的var,就会得到不同的结果:

    1. let x = 3;
    2. function func(randomize) {
    3. if (randomize) {
    4. let x = Math.random();
    5. return x;
    6. }
    7. return x;
    8. }
    9. func(false); // 3

    因此,盲目地用let或const代替var是有风险的,我的建议是:
    1、只在新代码中使用let或const
    2、丢弃旧代码或仔细认证

    更多信息:es6中的变量和作用域

    3.从IIFEs到代码块

    在es5中,如果要维护本地变量,不得不使用IIFE:

    1. (function () { // open IIFE
    2. var tmp = ···;
    3. ···
    4. }()); // close IIFE
    5. console.log(tmp); // ReferenceError

    而在es6中,则只需要代码块和let声明:

    1. { // open block
    2. let tmp = ···;
    3. ···
    4. } // close block
    5. console.log(tmp); // ReferenceError

    更多信息:避免IIFEs

    4.从字符串拼接到模板常量

    在es6中,对于字符串插值和多行字符串,Javscript能得到其字面值。

    4.1 字符串插值

    在es5中,是将结果放在字符串中进行拼接:

    1. function printCoord(x, y) {
    2. console.log('('+x+', '+y+')');
    3. }

    es6中,通过字符串字面模板,可以在字符串中插入变量值:

    1. function printCoord(x, y) {
    2. console.log(`(${x}, ${y})`);
    3. }
    4.2 多行字符串

    模板字面量也能输出多行字符串。
    例如,在es5中,输出多行字符串,得这样:

    1. var HTML5_SKELETON =
    2. '<!doctype html>\n' +
    3. '<html>\n' +
    4. '<head>\n' +
    5. ' <meta charset="UTF-8">\n' +
    6. ' <title></title>\n' +
    7. '</head>\n' +
    8. '<body>\n' +
    9. '</body>\n' +
    10. '</html>\n';

    如果通过反斜线转义新行,代码看起来会舒服点(但还是要显示的添加新行):

    1. var HTML5_SKELETON = '\
    2. <!doctype html>\n\
    3. <html>\n\
    4. <head>\n\
    5. <meta charset="UTF-8">\n\
    6. <title></title>\n\
    7. </head>\n\
    8. <body>\n\
    9. </body>\n\
    10. </html>';

    而es6得模板字面量能跨多行:

    1. const HTML5_SKELETON = `
    2. <!doctype html>
    3. <html>
    4. <head>
    5. <meta charset="UTF-8">
    6. <title></title>
    7. </head>
    8. <body>
    9. </body>
    10. </html>`;

    更多信息:字面量模板

    5.从函数表达式到箭头函数

    在es5中,在函数表达式中必须小心使用this。在示例中,我创建了辅助变量_this_,以便于我能再line B处使用:

    1. function UiComponent {
    2. var _this = this; // (A)
    3. var button = document.getElementById('myButton');
    4. button.addEventListener('click', function () {
    5. console.log('CLICK');
    6. _this.handleClick(); // (B)
    7. });
    8. }
    9. UiComponent.prototype.handleClick = function () {
    10. ···
    11. };

    在es6中,可以使用箭头函数,它不会影响this:

    1. class UiComponent {
    2. constructor() {
    3. let button = document.getElementById('myButton');
    4. button.addEventListener('click', () => {
    5. console.log('CLICK');
    6. this.handleClick(); // (A)
    7. });
    8. }
    9. handleClick() {
    10. ···
    11. }
    12. }

    对于只返回结果的短小回调函数,箭头函数是非常便利的。
    在es5中,如下的回调是相对冗长的:

    1. var arr = [1, 2, 3];
    2. var squares = arr.map(function (x) { return x * x });

    在es6中,箭头函数会更简洁:

    1. let arr = [1, 2, 3];
    2. let squares = arr.map(x => x * x);

    在定义参数时,如果只有一个参数,括号是可以省略的。因而,(x) => x * x and x => x * x 都是允许的。
    更多信息:箭头函数

    6.处理多个返回值

    一些函数或方法能通过数组或对象返回多个值。在es5中,总是需要创建中间变量来访问返回值。es6中,可以使用解构。

    6.1 通过数组返回多个值

    exec()返回类数组对象的捕获组。es5中,当要访问捕获组的值时,需要一个中间变量:

    1. var matchObj =
    2. /^(\d\d\d\d)-(\d\d)-(\d\d)$/
    3. .exec('2999-12-31');
    4. var year = matchObj[1];
    5. var month = matchObj[2];
    6. var day = matchObj[3];

    es6中,解构使代码更简单:

    1. let [, year, month, day] =
    2. /^(\d\d\d\d)-(\d\d)-(\d\d)$/
    3. .exec('2999-12-31');

    空得逗号表示跳过数组的第一个元素。

    6.2 通过对象返回多个值

    Object.getOwnPropertyDescriptor()会返回一个属性描述符对象。

    es5中,仍需要一个中间变量来访问你感兴趣的对象属性值:

    1. var obj = { foo: 123 };
    2. var propDesc = Object.getOwnPropertyDescriptor(obj, 'foo');
    3. var writable = propDesc.writable;
    4. var configurable = propDesc.configurable;
    5. console.log(writable, configurable); // true true

    es6,可以使用解构:

    1. let obj = { foo: 123 };
    2. let {writable, configurable} =
    3. Object.getOwnPropertyDescriptor(obj, 'foo');
    4. console.log(writable, configurable); // true true

    {writable, configurable}的值如下:

    1. { writable: writable, configurable: configurable }

    更多信息:解构

    7.从for到forEach再到for-of

    先说es6,一般会这样迭代数组:

    1. var arr = ['a', 'b', 'c'];
    2. for (var i=0; i<arr.length; i++) {
    3. var elem = arr[i];
    4. console.log(elem);
    5. }

    也可以使用Array的forEach:

    1. arr.forEach(function (elem) {
    2. console.log(elem);
    3. });

    for循环的优点是可以中断,forEach的优点是简洁。
    而es6的for-of循环则结合了两者的优点:

    1. let arr = ['a', 'b', 'c'];
    2. for (let elem of arr) {
    3. console.log(elem);
    4. }

    for-of循环也能通过数组的entries方法和解构返回数组的索引和对应的值:

    1. for (let [index, elem] of arr.entries()) {
    2. console.log(index+'. '+elem);
    3. }

    更多信息:for-of循环

    8. 参数默认值

    es5中,指定参数默认值得这样:

    1. function foo(x, y) {
    2. x = x || 0;
    3. y = y || 0;
    4. ···
    5. }

    es6有个更简洁的语法:

    1. function foo(x=0, y=0) {
    2. ···
    3. }

    es6语法还一个优点:参数默认值只能被undefined触发,而在es5中,则会被任何z为false的值触发。
    更多信息:参数默认值

    9.命名参数

    在Javascript中,命名参数的普遍方式是对象字面量:

    1. selectEntries({ start: 0, end: -1 });

    其等价实现:

    1. function selectEntries(options) {
    2. var start = options.start || 0;
    3. var end = options.end || -1;
    4. var step = options.step || 1;
    5. ···
    6. }

    es6中,解构是语法更简单:

    1. function selectEntries({ start=0, end=-1, step=1 }) {
    2. ···
    3. }
    9.1 使参数可选

    es5中使参数可选的做法是这样的:

    1. function selectEntries(options) {
    2. options = options || {}; // (A)
    3. var start = options.start || 0;
    4. var end = options.end || -1;
    5. var step = options.step || 1;
    6. ···
    7. }

    es6中,可以指定{}为参数的默认值:

    1. function selectEntries({ start=0, end=-1, step=1 } = {}) {
    2. ···
    3. }

    更多信息::模拟命名参数

    10.从arguments到参数扩展

    es5中,若想让方法或函数接受任意个数的参数,就必须使用指定的arguments变量:

    1. function logAllArguments() {
    2. for (var i=0; i < arguments.length; i++) {
    3. console.log(arguments[i]);
    4. }
    5. }

    es6中,可以使用...操作达到同样地效果:

    1. function logAllArguments(...args) {
    2. for (let arg of args) {
    3. console.log(arg);
    4. }
    5. }

    还有更nice的语法:

    1. function format(pattern, ...args) {
    2. ···
    3. }

    而es5中的处理则相对笨拙:

    1. function format() {
    2. var pattern = arguments[0];
    3. var args = arguments.slice(1);
    4. ···
    5. }

    更多信息:Rest parameters

    11.从apply到散布操作符(…)

    es5中,apply()会将数组转会成参数,es6中使用散布操作符达到同样地目的。

    11.1 Math.max()

    es5–>apply():

    1. > Math.max.apply(null, [-1, 5, 11, 3])
    2. 11

    es6–>spread operator:

    1. > Math.max(...[-1, 5, 11, 3])
    2. 11
    11.2 Array.prototype.push()

    es5–>apply():

    1. var arr1 = ['a', 'b'];
    2. var arr2 = ['c', 'd'];
    3. arr1.push.apply(arr1, arr2);
    4. // arr1 is now ['a', 'b', 'c', 'd']

    es6–>spread operator:

    1. let arr1 = ['a', 'b'];
    2. let arr2 = ['c', 'd'];
    3. arr1.push(...arr2);
    4. // arr1 is now ['a', 'b', 'c', 'd']

    更多信息:spread operator

    从concat()到(…)

    ES5 – concat():

    1. var arr1 = ['a', 'b'];
    2. var arr2 = ['c'];
    3. var arr3 = ['d', 'e'];
    4. console.log(arr1.concat(arr2, arr3));
    5. // [ 'a', 'b', 'c', 'd', 'e' ]

    ES6 – spread operator:

    1. let arr1 = ['a', 'b'];
    2. let arr2 = ['c'];
    3. let arr3 = ['d', 'e'];
    4. console.log([...arr1, ...arr2, ...arr3]);
    5. // [ 'a', 'b', 'c', 'd', 'e' ]

    更多信息:spread operator

    13.从构造器到类

    对于构造器语法,es6的类则更简便。

    13.1 基本类

    es5中实现一个基本类如下:

    1. function Person(name) {
    2. this.name = name;
    3. }
    4. Person.prototype.describe = function () {
    5. return 'Person called '+this.name;
    6. };

    es6中,类提供了更简洁的语法:

    1. class Person {
    2. constructor(name) {
    3. this.name = name;
    4. }
    5. describe() {
    6. return 'Person called '+this.name;
    7. }
    8. }
    13.2 派生类

    es5实现了类的派生,下面是实现派生类的一种规范方法:

    1. function Employee(name, title) {
    2. Person.call(this, name); // super(name)
    3. this.title = title;
    4. }
    5. Employee.prototype = Object.create(Person.prototype);
    6. Employee.prototype.constructor = Employee;
    7. Employee.prototype.describe = function () {
    8. return Person.prototype.describe.call(this) // super.describe()
    9. + ' (' + this.title + ')';
    10. };

    es6内置了类派生语法,要借助extends关键字:

    1. class Employee extends Person {
    2. constructor(name, title) {
    3. super(name);
    4. this.title = title;
    5. }
    6. describe() {
    7. return super.describe() + ' (' + this.title + ')';
    8. }
    9. }

    更多信息:类

    14.从自定义error到Error派生

    跟上面有点类似。es5中自定义error:

    1. function MyError() {
    2. // Use Error as a function
    3. var superInstance = Error.apply(null, arguments);
    4. copyOwnPropertiesFrom(this, superInstance);
    5. }
    6. MyError.prototype = Object.create(Error.prototype);
    7. MyError.prototype.constructor = MyError;

    es6通过派生实现:

    1. class MyError extends Error {
    2. }

    更多信心:Subclassing built-in constructors

    15.从对象字面量的函数表达式和方法定义

    语法上的差别。es5实现:

    1. var obj = {
    2. foo: function () {
    3. ···
    4. },
    5. bar: function () {
    6. this.foo();
    7. }, // trailing comma is legal in ES5
    8. }

    es6:

    1. let obj = {
    2. foo() {
    3. ···
    4. },
    5. bar() {
    6. this.foo();
    7. },
    8. }

    更多信息:方法定义

    16.从对象到图

    es5中利用对象来实现图的数据结构,需要将对象的prototype指向null,并保证__proto__上没有对应的键。

    1. var dict = Object.create(null);
    2. function countWords(word) {
    3. var escapedWord = escapeKey(word);
    4. if (escapedWord in dict) {
    5. dict[escapedWord]++;
    6. } else {
    7. dict[escapedWord] = 1;
    8. }
    9. }
    10. function escapeKey(key) {
    11. if (key.indexOf('__proto__') === 0) {
    12. return key+'%';
    13. } else {
    14. return key;
    15. }
    16. }

    es6则内置了Map数据结构;

    1. let map = new Map();
    2. function countWords(word) {
    3. let count = map.get(word) || 0;
    4. map.set(word, count + 1);
    5. }

    更多信息:Maps and Sets

    17.从CommonJS模块到es6 模块

    es5中,模块系统是基于AMD或CommocJS语法。es6内置了模块语法,但并没有得到Javascript引擎良好支持。

    17.1 多个导出

    在CommonJS中,可以这样实现:

    1. //------ lib.js ------
    2. var sqrt = Math.sqrt;
    3. function square(x) {
    4. return x * x;
    5. }
    6. function diag(x, y) {
    7. return sqrt(square(x) + square(y));
    8. }
    9. module.exports = {
    10. sqrt: sqrt,
    11. square: square,
    12. diag: diag,
    13. };
    14. //------ main1.js ------
    15. var square = require('lib').square;
    16. var diag = require('lib').diag;
    17. console.log(square(11)); // 121
    18. console.log(diag(4, 3)); // 5

    es6的语法是酱紫的:

    1. //------ lib.js ------
    2. export const sqrt = Math.sqrt;
    3. export function square(x) {
    4. return x * x;
    5. }
    6. export function diag(x, y) {
    7. return sqrt(square(x) + square(y));
    8. }
    9. //------ main1.js ------
    10. import { square, diag } from 'lib';
    11. console.log(square(11)); // 121
    12. console.log(diag(4, 3)); // 5

    或者作为一个对象导入:

    1. //------ main2.js ------
    2. import * as lib from 'lib'; // (A)
    3. console.log(lib.square(11)); // 121
    4. console.log(lib.diag(4, 3)); // 5
    17.2 单个导出

    Node.js继承了CommonJS的语法,能从模块导出单个值:

    1. //------ myFunc.js ------
    2. module.exports = function () { ··· };
    3. //------ main1.js ------
    4. var myFunc = require('myFunc');
    5. myFunc();

    es6通过export default实现:

    1. //------ myFunc.js ------
    2. export default function () { ··· } // no semicolon!
    3. //------ main1.js ------
    4. import myFunc from 'myFunc';
    5. myFunc();

    更多信息:Modules

    相关文章:ECMAScript 6新特性介绍

    本文根据@Rauschmayer的《Getting started with ECMAScript 6》所译,整个译文带有我自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://www.2ality.com/2015/08/getting-started-es6.html

    淡忘~浅思猜你喜欢

    ECMAScript 6新特性介绍

    js:简单的拖动效果

    【译】Impress.js制作酷炫Presentation PPT

    一个js闭包问题的解答

    问题:关于一个贴友的js留言板的实现
    无觅

    转载请注明:淡忘~浅思 » Getting started with ECMAScript 6



沪ICP备19023445号-2号
友情链接