【在主画面加入捷径】
       
【选择语系】
繁中 简中

[书籍回顾] Practical ES6 评价

REVIEW
【赞助商连结】

    前言

    Practical ES6 是 SitePoint 所出版的 the Modern JavaScript Collection 套书中的第一本。该套书共有四本:

    JavaScript 已经问世 20 多年了,在这段时间内,JavaScript 和网页技术产生许多的变化。该系列书籍期望能带来到西元 2018 年为止的最新实务。Practical ES6 注重的是 JavaScript 语法本身,至于工具链或其他的部分,则要参考该系列其他的书籍。

    本书共 221 页,分为 19 章,算是一本轻量级的书籍。我们接下来会逐一介绍各章节。有些读者可能会想到有些浏览器并未完全实现 ES6+ 的特性,我们能把这些新的语法特性套在自己的项目上吗?透过 Babel 我们可以把 ES6+ 程序代码转为等效的 ES5 程序代码,故我们可以在项目中使用 ES6+ 特性。Babel 是本系列另一本书 Modern JavaScript Tools and Skill 的重点之一,故本书不重覆篇幅。

    Chapter 1: New Keywords let and const

    在 ES5 以前,JavaScript 用 var 声明变量,但 var 有一些不良的特性:

    • 可视域 (scoping)
    • 重覆声明不会引发错误
    • 无法做为实质上的常数

    为了改善这些不良特性,故引入 letconst。除了兼容于旧浏览器外,应优先使用 letconst

    Chapter 2: Using Map, Set, WeakMap, WeakSet

    在 ES5 以前,JavaScript 用物件实字 {} 做为映射 (map) 或集合 (set),但这样做有一些显着的缺点:

    • 键 (key) 一定要是字串
    • 无法有效地迭代
    • 和内建方法 (method) 相冲突

    在 ES6 后引入 MapSet 两种新的物件,用来改善物件实字的缺点。至于 WeakMapWeakSet 则是在可能出现循环引用的情境中替代 MapSet 的物件,用法上几无差异。

    Chapter 3: New Arrap.* and Array.prototype.* Methods

    本章简短地介绍 ES6 中对 Array 物件增强的特性:

    • Array.from()
    • Array.prototype.find()
    • Array.prototype.findIndex()
    • Array.prototype.keys()
    • Array.prototype.values()
    • Array.prototype.fill()

    Array 本质上没有改变,只是多些方法 (method),大略看一下即可。

    Chapter 4: New String Methods - String.prototype.*

    承续上一章的精神,本章简短地介绍 ES6 中对 String 物件增强的特性:

    • String.prototype.startsWith()
    • String.prototype.endsWith()
    • String.prototype.includes()
    • String.prototype.repeat()
    • String.raw()

    由于 String 没有什么大幅变动,只是新增一些方法,简单看一下即可。

    Chapter 5: New Number Methods

    承续前面章节的精神,本章简短地介绍 ES6 中对 Number 物件增强的特性:

    • Number.isInteger()
    • Number.isNan()
    • Number.isFinite()
    • Number.isSafeInteger()

    由此可知,ES6 加入一些 Number 可用的判断方法,对撰写程序带来一些便利性。

    Chapter 6: ES6 Arrow Functions: Fat and Concise Syntax in JavaScript

    Arrow函数 (arrow function) 是一个新的语法,主要用于撰写函式表达式 (function expression)。例如以下函式:

    $(document).ready(function () {
        // Implement your code here.
    });
    

    用纯 JavaScript 搭上 arrow函数改写如下:

    document.addEventListener('DOMContentLoaded', () => {
        // Implement your code here.
    }, false);
    

    Arrow函数的用意在让 JavaScript 的语法更简洁,但两者有一些些差异:

    • this 的作用方式相异
    • Arrow函数不能做为构造函数 (constructor)
    • Arrow函数不能做为生成器 (generator)
    • Arrow函数没有 arguments 物件

    由于 arrow函数的用意是让程序代码简化,不是必备的功能,读者可自行决定是否要在项目中使用这项特性。

    Chpater 7: Symbols and Their Use

    Symbol 是 ES6 引入的新特性,主要是做为独一无二的 (unique) 符号,但其值不重要。如下例:

    let EAST = Symbol(),
        WEST = Symbol(),
        NORTH = Symbol(),
        SOUTH = Symbol();
    

    在 ES5 以前,我们会直接塞入字串或其他值,但那样做无法表达该变量独一无二的性质,Symbol 物件的出现,就是为了解决这个议题。本章介绍数个 Symbol 的使用情境,有兴趣的读者可以读一读。

    Chapter 8: How to Use Proxies

    Proxy 是 ES6 的新特性,目的是在 JavaScript 中写元程序 (meta-programming)。透过 proxy,程序人可以透过程序去修改物件的行为。由于元程序是相对抽象的概念,本章有一些范例,可透过范例来了解如何使用 proxy。

    Proxy 包括三个要件:

    • Target:Proxy 要修改的物件
    • Handler:实现 proxy 的行为的物件,通常是物件实字
    • Traps:在 handler 中定义的函式,用来修改 target 的行为。

    以下范例摘自 MDN:

    var handler = {
        get: function(obj, prop) {
            return prop in obj ?
                obj[prop] :
                37;
        }
    };
    
    var p = new Proxy({}, handler);
    p.a = 1;
    p.b = undefined;
    
    console.log(p.a, p.b); // 1, undefined
    console.log('c' in p, p.c); // false, 37
    

    在本例中,我们修改 get函数,当属性不存时,回传 37

    当然,proxy 算是相对困难的技巧,本章提供更多的说明和范例,有兴趣的读者可以读一读。由于 proxy 算是进阶的特性,无法用 polyfill 来补,也无法用 Babel 来转,如果项目要支援 Internet Explorer (IE),就应该避开 proxy。

    Chapter 9: Destructing Assignment

    解构指派 (destructing assignment) 算是一种指派运算的语法糖,透过这项特性可以简化程序代码。像是以下的数组:

    let [a, b, c] = [3, 4, 5];
    

    或是以下的物件实字:

    let {one: a, two: b, three: c} = {one: "eins", two: "zwei", three: "drei"};
    

    由于指派算是一个较小的特性,本章的重点放在各种透过解构简化程序的情境,可以读一读,学习这些小技巧。

    Chapter 10: ES6 Generators and Iterators: a Developer’s Guide

    迭代器 (iterators) 和生成器 (generators) 是相当实用的特性。迭代器的使用方式如下:

    for (const element of collection) {
        // Do something here.
    }
    

    在 ES6 之后,我们可以不用索引来迭代容器,可以直接走访容器。

    生成器是用来产生迭代器的函式,例如下列用来生成费伯那西数的生成器:

    function* getNextFib() {
        let a = 0;
        let b = 1;
        let c;
        
        while (true) {
            yield a;
            c = a + b;
            a = b;
            b = c;
        }
    }
    
    const fib = getNextFib();
    
    for (let i = 0; i < 10; i++) {
        console.log(fib.next().value);
    }
    

    本章对迭代器和生成器有较详细的说明,有兴趣的读者可以读一下。

    Chapter 11: Object-Oriented JavaScript: A Deep Dive into ES6 Classes

    class 算是 ES6 里主要的新增特性之一。JavaScript 的物件是以原型 (prototype) 为基础,引入 class 并不代表物件系统有所改变,而是用更好的语法来实现物件。在 ES6 之后,就不建议用旧有的建构函式那套手法来建立物件,除非是要维护旧项目。本章介绍数个 class 相关的议题:

    • 新式构造函数 (constructor)
    • 建立私有属性的手法
    • 静态属性和方法
    • 继承 (单一继承)
    • 达成多重继承的手法

    这章算是值得一读的章节。

    Chapter 12: Understand ES6 Modules

    模块算是 ES6 引入的新特性,原本的 JavaScript 的模块如下:

    • 网页前端
    • 网页后端
      • CommonJS
      • 用 bundler 将 JavaScript 程序代码打包成单一脚本,像是 webpack
    • 网页前后端通用

    前述所提的都是在 ES6 模块出现前的一些 JavaScript 的模块机制。本章介绍如何在项目中加入模块。

    虽然 ES6 模块立意良好,但模块会造成项目架构的改变,且有一定比例的浏览器不支援模块 (参考这里),势必要写退路 (fallback)。甚至本书作者也建议在现阶段的项目中,不一定要急着加入模块,等过一阵子模块更普遍再使用也不迟。

    Chapter 13: An Overview of JavaScript Promises

    Promises 是 ES6 里重要的新特性,由于 JavaScript 程序以异步模式来运行,有时会写出复杂、难维护的回调 (callback) 程序;透过 promises 可使得程序代码更简洁易读,一些常见的情境就是包装 AJAX 程序,jQuery 新版本中也加入以 promise 为基础的 API 调用,如下例:

    var jqxhr = $.post( "example.php", function() {
      alert( "success" );
    })
      .done(function() {
        alert( "second success" );
      })
      .fail(function() {
        alert( "error" );
      })
      .always(function() {
        alert( "finished" );
      });
    

    本章中有更多 promise 的写法,有兴趣的读者可以读一读。Promise 让程序代码更易写易读,是值得学习的新特性。

    Chapter 14: JavaScript Decorators: What They are and When to Use Them

    Decorator 在 Angular 和 TypeScript 中很流行,但在 JavaScript 中这项特性还没稳定下来 (参考这里),故笔者目前不建议使用这样特性。有兴趣的读者可以自行阅读。

    Chapter 15: Enhanced Object Literals

    本章介绍一些在 ES6 以后对于物件实字 {} 的增强:

    • 直接从变量初始化物件
    • 从物件实字建立函式的语法糖
    • 动态属性 (dynamic property)
    • 物件解构 (object destructing)
    • Rest/spread 属性

    这章有些内容和其他章节有一些重覆,可能有些内容很难归类所造成。

    Chapter 16: Introduction to the Fetch API

    Fetch API 是新的抓取数据的 API,有点像先前的 XMLHttpRequest 物件,但加入一些新的功能,像是跨网域存取 (CORS) 及 HTTP origin 标头等。本章介绍如何以 fetch API 搭配 promise 或 async/await,另外介绍 fetch API 发生错误的处理方式。

    虽然部分浏览器不支援 fetch API 但有 polyfill 可用,除了要支援较旧的浏览器外,应该可以放心使用此 API。

    Chapter 17: ES6 (ES2015) and Beyond: Understand JavaScript Versioning

    本章较偏历史面及政策面,技术含金量相对低。一开始讲到 JavaScript 的由来及标准化的过程,之后介绍一个语言的提案 (proposal) 制定的过程。本章的重点是看懂 JavaScript 的版本号:

    (补充表格)

    • N/A、ECMAScript 1、N/A
    • N/A、ECMAScript 2、N/A
    • N/A、ECMAScript 3、N/A
    • N/A、ECMAScript 5、ES5
    • ES2015、ECMAScript 2015、ES6
    • ES2016、ECMAScript 2016、ES7
    • ES2017、ECMAScript 2017、ES8
    • ES2018、ECMAScript 2018、ES9

    注:ECMAScript 4 遭废弃而未发布。

    我们在谈现代 JavaScript 是指 ES6 之后所新增的语法特性;相对来说,ES5 会当成浏览器实现的最低版本,也是 Babel 转换的目标版本。

    Chapter 18: What’s New in ES2017: Async Functions, Improved Objects, and More

    本章简短地介绍 ES2017 带来的新特性:

    • asyncawait
    • Object 的新方法 (methos),包括 Object.values()Object.entries()Object.getOwnPropertyDescriptors()
    • 字串的新方法,包括 padStart()padEnd()

    ES2017 最重要改变的是 async 语法,可以让异步程序写起来更简洁,其他的部分简单看一下即可。

    Chapter 19: What’s New in ES2018

    承续上一章,本章简短地介绍 ES2018 的新特性:

    • 异步迭代 (asynchronous iteration)
    • Promise.finally()
    • 用于物件解构 (object destructing) 的 rest/spread 语法
    • 一些对正规表示式的小幅强化

    由此可看出,ECMAScript 持续改善异步程序的写法。

    结语

    由本书的写作方式可看出,本书比较适合已经会一些些 JavaScript 但想帮自己充电的程序人;对于程序新手或是完全不懂 JavaScript 的读者来说,本书读起来可能会过于吃力,而且无法理解为什么 JavaScript 要引入这些新特性。

    本书的范例是典型的西方式写法,没有完整的范例而是短小的程序代码片段 (code snippets)。对于有经验的程序人来说,这样的书很容易抓到重点,但初学者可能觉得这样的写作方式过于困难。除了针对完全新手的书籍外,大部分书籍都会采用这种写作方式,要慢慢习惯。

    本书承袭 SitePoint 系列书籍轻薄短小的特性,阅读一次应该不会花太久的时间。但 JavaScript 有许多的议题,仅以 200 多页的份量是无法完整交待的,所以 the Modern JavaScript Collection 这个系列以四本书的容量来介绍 JavaScript,读者可视自己的需求单独购买或成套购买。

    如何购买

    如果读者想购买本书,可以参考以下的链接。读者可依自己需求选择要单买或是整套买。