Introduction to ECMAScript 6
Note: This chapter contains the author's personal study notes based on the original tutorial, sourced from http://es6.ruanyifeng.com/ (opens new window). All tutorial copyrights belong to the original author.
# Introduction to ECMAScript 6
ECMAScript 6.0 (hereafter referred to as ES6) is the next-generation standard for the JavaScript language, officially released in June 2015. Its goal is to make JavaScript capable of writing complex, large-scale applications and to serve as an enterprise-level development language.
# The Relationship Between ECMAScript and JavaScript
A common question is: what exactly is the relationship between ECMAScript and JavaScript?
To clarify this, we need to look at the history. In November 1996, Netscape, the creator of JavaScript, decided to submit JavaScript to the standards organization ECMA, hoping the language could become an international standard. The following year, ECMA published the first edition of standard document 262 (ECMA-262), which defined standards for browser scripting languages and named this language ECMAScript. This version was 1.0.
The standard was designed for JavaScript from the beginning, but there are two reasons why it wasn't called JavaScript. First, there was the trademark issue -- Java is a trademark of Sun Microsystems, and under the licensing agreement, only Netscape could legally use the name JavaScript, which Netscape had also registered as a trademark. Second, the intent was to show that the language was standardized by ECMA, not Netscape, which would help ensure the language's openness and neutrality.
Therefore, the relationship between ECMAScript and JavaScript is that the former is the specification of the latter, and the latter is one implementation of the former (other ECMAScript dialects include JScript and ActionScript). In everyday usage, the two terms are interchangeable.
# The Relationship Between ES6 and ECMAScript 2015
The term ECMAScript 2015 (abbreviated as ES2015) is also frequently seen. What is its relationship with ES6?
In 2011, after the release of ECMAScript 5.1, work began on version 6.0. Thus, the original meaning of ES6 referred to the next version of JavaScript.
However, because this version introduced too many syntax features and many organizations and individuals kept submitting new features during the drafting process, it quickly became clear that it would be impossible to include all planned features in a single version. The conventional approach would be to release version 6.0 first, then 6.1 after some time, followed by 6.2, 6.3, and so on.
But the standards committee didn't want to do it that way. They wanted the standard upgrade process to become routine: anyone could submit syntax proposals to the standards committee at any time, and the committee would meet monthly to evaluate whether these proposals were acceptable and what improvements were needed. If a proposal was mature enough after multiple meetings, it could officially become part of the standard. This meant that version upgrades became a continuous rolling process, with changes happening every month.
The standards committee ultimately decided that the standard would be officially released once a year in June, becoming the official version for that year. From then on, changes would be made on top of that version until the following June, when the draft would naturally become the new year's version. This way, version numbers were no longer needed -- year-based labels sufficed.
The first version of ES6 was released in June 2015, officially named "The ECMAScript 2015 Standard" (abbreviated as ES2015). In June 2016, "The ECMAScript 2016 Standard" (abbreviated as ES2016) was released on schedule with minor revisions. This version can be considered ES6.1, since the differences are very small (only the includes method for array instances and the exponentiation operator were added), essentially making it the same standard. According to plan, ES2017 was released in June 2017.
Therefore, ES6 is both a historical term and a general reference, meaning the next-generation standard for JavaScript after version 5.1, covering ES2015, ES2016, ES2017, and so on. ES2015 is the official name, specifically referring to the version of the language standard released that year. In this book, references to ES6 generally mean the ES2015 standard, but sometimes refer broadly to "next-generation JavaScript."
# The Approval Process for Syntax Proposals
Anyone can submit proposals to the standards committee (also known as TC39) requesting changes to the language standard.
A new syntax goes through five stages from proposal to official standard. Changes at each stage must be approved by the TC39 committee.
- Stage 0 - Strawman
- Stage 1 - Proposal
- Stage 2 - Draft
- Stage 3 - Candidate
- Stage 4 - Finished
Once a proposal reaches Stage 2, it is almost certain to be included in a future official standard. All current ECMAScript proposals can be viewed on TC39's official website GitHub.com/tc39/ecma262 (opens new window).
One of the goals of this book is to track the latest developments in the ECMAScript language and introduce all new syntax since version 5.1. All new syntax that is confirmed or very likely to be included in the standard will be covered.
# History of ECMAScript
ES6 took a full 15 years from initial drafting to final release.
As mentioned earlier, ECMAScript 1.0 was released in 1997, followed by ECMAScript 2.0 (June 1998) and ECMAScript 3.0 (December 1999) in the next two years. Version 3.0 was a huge success, gaining widespread industry support and becoming the prevailing standard. It established the basic syntax of the JavaScript language, which all subsequent versions fully inherited. Even today, beginners learning JavaScript are essentially learning version 3.0 syntax.
In 2000, ECMAScript 4.0 began to take shape. This version was ultimately not approved, but much of its content was inherited by ES6. Therefore, the starting point for ES6 was actually the year 2000.
Why wasn't ES4 approved? Because the version was too radical -- it completely overhauled ES3, causing some members of the standards committee to refuse to accept it. ECMA's Technical Committee 39 (TC39) is responsible for developing the ECMAScript standard, with members including major companies such as Microsoft, Mozilla, and Google.
In October 2007, the ECMAScript 4.0 draft was published, originally expected to be officially released in August of the following year. However, serious disagreements arose over whether to approve this standard. Major companies led by Yahoo, Microsoft, and Google opposed a major upgrade to JavaScript, advocating for small changes; while Mozilla, led by JavaScript creator Brendan Eich, insisted on the current draft.
In July 2008, because of the deep disagreements and heated debates over what features the next version should include, ECMA met and decided to halt development of ECMAScript 4.0, release a small portion related to improvements of existing features as ECMAScript 3.1, and set aside the other ambitious ideas for future versions. Due to the atmosphere of the meeting, the project was codenamed Harmony. Shortly after, ECMAScript 3.1 was renamed ECMAScript 5.
In December 2009, ECMAScript 5.0 was officially released. The Harmony project was split in two: some of the more feasible ideas were named JavaScript.next and continued development, eventually becoming ECMAScript 6; the less mature ideas were designated as JavaScript.next.next, to be considered for the more distant future. TC39's overall plan was that ES5 would maintain basic compatibility with ES3, while major syntax revisions and new features would be completed by JavaScript.next. At that time, JavaScript.next referred to ES6; after the sixth edition was released, it would refer to ES7. TC39 predicted that ES5 would become the mainstream JavaScript development standard by mid-2013 and would maintain that position for five years.
In June 2011, ECMAScript 5.1 was released and became an ISO international standard (ISO/IEC 16262:2011).
In March 2013, the ECMAScript 6 draft was frozen, with no more new features to be added. New feature ideas would be placed in ECMAScript 7.
In December 2013, the ECMAScript 6 draft was published, followed by a 12-month discussion period to gather feedback.
In June 2015, ECMAScript 6 was officially approved and became an international standard. Counting from the year 2000, 15 years had passed.
# Deployment Progress
Support for ES6 in the latest versions of major browsers can be checked at kangax.github.io/compat-table/es6/ (opens new window). Over time, support has been increasing, with over 90% of ES6 syntax features now implemented.
Node is a server-side runtime environment for JavaScript. Its support for ES6 is even higher. Besides features that are enabled by default, some syntax features have been implemented but are not enabled by default. The following commands can be used to view the ES6 features that Node has implemented.
// Linux & Mac
$ node --v8-options | grep harmony
// Windows
$ node --v8-options | findstr harmony
2
3
4
5
I wrote a tool called ES-Checker (opens new window) to check ES6 support in various runtime environments. Visit ruanyf.github.io/es-checker (opens new window) to see the degree of ES6 support in your browser. Run the following commands to check the degree of ES6 support in the Node environment you are using.
$ npm install -g es-checker
$ es-checker
=========================================
Passes 24 feature Detections
Your runtime supports 57% of ECMAScript 6
=========================================
2
3
4
5
6
7
# Babel Transpiler
Babel (opens new window) is a widely used ES6 transpiler that can convert ES6 code to ES5 code for execution in current environments. This means you can write programs using ES6 syntax without worrying about whether the current environment supports it. Here is an example.
// Before transpilation
input.map(item => item + 1);
// After transpilation
input.map(function (item) {
return item + 1;
});
2
3
4
5
6
7
The original code above uses an arrow function. Babel converts it to a regular function, enabling it to run in JavaScript environments that don't support arrow functions.
The following command installs Babel in the project directory.
$ npm install --save-dev @babel/core
# Configuration File .babelrc
Babel's configuration file is .babelrc, placed in the project's root directory. The first step in using Babel is to configure this file.
This file is used to set transpilation rules and plugins, with the following basic format.
{
"presets": [],
"plugins": []
}
2
3
4
The presets field sets the transpilation rules. The official rule sets are available below, and you can install them as needed.
# Latest transpilation rules
$ npm install --save-dev @babel/preset-env
# React transpilation rules
$ npm install --save-dev @babel/preset-react
2
3
4
5
Then, add these rules to .babelrc.
{
"presets": [
"@babel/env",
"@babel/preset-react"
],
"plugins": []
}
2
3
4
5
6
7
Note that the use of all Babel tools and modules described below requires a properly configured .babelrc file.
# Command Line Transpilation
Babel provides the command-line tool @babel/cli for command-line transpilation.
It can be installed with the following command.
$ npm install --save-dev @babel/cli
Basic usage is as follows.
# Output transpilation result to standard output
$ npx babel example.js
# Write transpilation result to a file
# --out-file or -o specifies the output file
$ npx babel example.js --out-file compiled.js
# Or
$ npx babel example.js -o compiled.js
# Transpile an entire directory
# --out-dir or -d specifies the output directory
$ npx babel src --out-dir lib
# Or
$ npx babel src -d lib
# -s parameter generates a source map file
$ npx babel src -d lib -s
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# babel-node
The babel-node command from the @babel/node module provides a REPL environment with ES6 support. It supports all features of Node's REPL environment and can run ES6 code directly.
First, install the module.
$ npm install --save-dev @babel/node
Then, run babel-node to enter the REPL environment.
$ npx babel-node
> (x => x * 2)(1)
2
2
3
The babel-node command can directly run ES6 scripts. Put the above code in a script file es6.js, then run it directly.
# Code in es6.js
# console.log((x => x * 2)(1));
$ npx babel-node es6.js
2
2
3
4
# @babel/register Module
The @babel/register module overrides the require command by adding a hook. After that, whenever require is used to load files with .js, .jsx, .es, and .es6 extensions, Babel will transpile them first.
$ npm install --save-dev @babel/register
When using it, you must first load @babel/register.
// index.js
require('@babel/register');
require('./es6.js');
2
3
Then, there is no need to manually transpile index.js.
$ node index.js
2
2
Note that @babel/register only transpiles files loaded by the require command, not the current file itself. Also, since it transpiles in real time, it is only suitable for use in development environments.
# babel API
If you need to call Babel's API to transpile code, use the @babel/core module.
var babel = require('@babel/core');
// String transpilation
babel.transform('code();', options);
// => { code, map, ast }
// File transpilation (async)
babel.transformFile('filename.js', options, function(err, result) {
result; // => { code, map, ast }
});
// File transpilation (sync)
babel.transformFileSync('filename.js', options);
// => { code, map, ast }
// Babel AST transpilation
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
For the configuration object options, refer to the official documentation at http://babeljs.io/docs/usage/options/ (opens new window).
Here is an example.
var es6Code = 'let x = n => n + 1';
var es5Code = require('@babel/core')
.transform(es6Code, {
presets: ['@babel/env']
})
.code;
console.log(es5Code);
// '"use strict";\n\nvar x = function x(n) {\n return n + 1;\n};'
2
3
4
5
6
7
8
9
In the above code, the first argument to the transform method is a string representing the ES6 code to be converted, and the second argument is the configuration object for the conversion.
# @babel/polyfill
By default, Babel only transpiles new JavaScript syntax, not new APIs. For example, global objects like Iterator, Generator, Set, Map, Proxy, Reflect, Symbol, Promise, and methods defined on global objects (such as Object.assign) are not transpiled.
For instance, ES6 added the Array.from method to the Array object. Babel will not transpile this method. To use it, you must use babel-polyfill to provide a polyfill for the current environment.
The installation command is as follows.
$ npm install --save-dev @babel/polyfill
Then, add the following line at the top of your script.
import '@babel/polyfill';
// Or
require('@babel/polyfill');
2
3
The list of APIs that Babel does not transpile by default is quite extensive. For a detailed list, check the definitions.js (opens new window) file of the babel-plugin-transform-runtime module.
# Browser Environment
Babel can also be used in browser environments, using the browser version provided by the @babel/standalone (opens new window) module by inserting it into a web page.
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
// Your ES6 code
</script>
2
3
4
Note that real-time transpilation of ES6 to ES5 on a web page affects performance. In production environments, pre-transpiled scripts should be loaded.
Babel provides a REPL online compiler (opens new window) that can convert ES6 code to ES5 code online. The converted code can be directly inserted into web pages as ES5 code.
# Traceur Transpiler
Google's Traceur (opens new window) transpiler can also convert ES6 code to ES5 code.
# Direct Insertion into Web Pages
Traceur allows ES6 code to be directly inserted into web pages. First, the Traceur library files must be loaded in the page header.
<script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script>
<script src="https://google.github.io/traceur-compiler/bin/BrowserSystem.js"></script>
<script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script>
<script type="module">
import './Greeter.js';
</script>
2
3
4
5
6
In the above code, there are 4 script tags. The first loads the Traceur library file, the second and third adapt the library for the browser environment, and the fourth loads the user script, which can contain ES6 code.
Note that the type attribute of the fourth script tag is module, not text/javascript. This is the marker that the Traceur compiler uses to identify ES6 code. The compiler will automatically transpile all code with type=module to ES5 before handing it to the browser for execution.
Besides referencing external ES6 scripts, ES6 code can also be placed directly in the web page.
<script type="module">
class Calc {
constructor() {
console.log('Calc constructor');
}
add(a, b) {
return a + b;
}
}
var c = new Calc();
console.log(c.add(4,5));
</script>
2
3
4
5
6
7
8
9
10
11
12
13
Under normal circumstances, the above code will print 9 to the console.
For precise control over Traceur's behavior, you can use the following parameter configuration approach.
<script>
// Create the System object
window.System = new traceur.runtime.BrowserTraceurLoader();
// Set some experimental options
var metadata = {
traceurOptions: {
experimental: true,
properTailCalls: true,
symbols: true,
arrayComprehension: true,
asyncFunctions: true,
asyncGenerators: exponentiation,
forOn: true,
generatorComprehension: true
}
};
// Load your module
System.import('./myModule.js', {metadata: metadata}).catch(function(ex) {
console.error('Import failed', ex.stack || ex);
});
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
In the above code, first the Traceur global object window.System is created, then System.import can be used to load ES6. When loading, a configuration object metadata must be passed in, whose traceurOptions property can configure ES6 feature support. Setting experimental: true enables support for experimental new features beyond ES6.
# Online Conversion
Traceur also provides an online compiler (opens new window) that can convert ES6 code to ES5 code online. The converted code can be directly inserted into web pages as ES5 code.
The above example converted to ES5 code looks like this.
<script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script>
<script src="https://google.github.io/traceur-compiler/bin/BrowserSystem.js"></script>
<script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script>
<script>
$traceurRuntime.ModuleStore.getAnonymousModule(function() {
"use strict";
var Calc = function Calc() {
console.log('Calc constructor');
};
($traceurRuntime.createClass)(Calc, {add: function(a, b) {
return a + b;
}}, {});
var c = new Calc();
console.log(c.add(4, 5));
return {};
});
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Command Line Conversion
When used as a command-line tool, Traceur is a Node module that first needs to be installed via npm.
$ npm install -g traceur
After successful installation, Traceur can be used from the command line.
Traceur directly runs ES6 script files and displays the results in standard output, using the earlier calc.js as an example.
$ traceur calc.js
Calc constructor
9
2
3
To save the transpiled ES6 script as ES5, use the following syntax.
$ traceur --script calc.es6.js --out calc.es5.js
The --script option in the above command specifies the input file, and the --out option specifies the output file.
To prevent compilation failures for some features, it's best to add the --experimental option.
$ traceur --script calc.es6.js --out calc.es5.js --experimental
Files generated by command-line conversion can be directly placed in a browser to run.
# Node Environment Usage
Traceur's Node usage is as follows (assuming the traceur module is already installed).
var traceur = require('traceur');
var fs = require('fs');
// Convert the ES6 script to a string
var contents = fs.readFileSync('es6-file.js').toString();
var result = traceur.compile(contents, {
filename: 'es6-file.js',
sourceMap: true,
// Other settings
modules: 'commonjs'
});
if (result.error)
throw result.error;
// The js property of the result object is the transpiled ES5 code
fs.writeFileSync('out.js', result.js);
// The sourceMap property corresponds to the map file
fs.writeFileSync('out.js.map', result.sourceMap);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20