1. 尝试ES6
这里有三种简朴地体式格局用于ES6编程:
Web浏览器:运用Babel REPL,能够将ES6编译成ES5的平台,而且并不须要装置。
敕令行:运用
babel-node
,能够实行ES6的Node.js版本(会在内部编译es5)。须要经由过程npm
装置。种种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
装置:
$ npm install --global babel
跟node一样,能够敕令开启REPL交互:
$ babel-node
一旦开启REPL,就能够去实行ES6代码:
> let arr = [1, 2, 3];
> arr.map(x => x * x)
[ 1, 4, 9 ]
注重:babel-node现在不支撑多行输入
Babel官网上有管多关于Babel CLI的东西。
2. 从var到let/const
es6有两种新的体式格局来声明变量:
1、let
用于声明块级作用于变量
2、const
用于声明常量,其值不能被转变。
let
或const
能够用来替换var
声明变量,然则不能自觉运用,由于差异的作用域会转变代码的行动。比方:
var x = 3;
function func(randomize) {
if (randomize) {
var x = Math.random(); // (A) scope: whole function
return x;
}
return x; // accesses the x from line A
}
func(false); // undefined
func()
会心外埠返回undefined
。你能够重写这部份代码,就晓得为何返回undefined
了:
var x = 3;
function func(randomize) {
var x;
if (randomize) {
x = Math.random();
return x;
}
return x;
}
func(false); // undefined
假如用let
替代之前的var
,就会获得差异的结果:
let x = 3;
function func(randomize) {
if (randomize) {
let x = Math.random();
return x;
}
return x;
}
func(false); // 3
因此,自觉地用let
或const
替代var
是有风险的,我的发起是:
1、只在新代码中运用let
或const
2、丢弃旧代码或细致认证
更多信息:es6中的变量和作用域
3. 从IIFEs到代码块
在es5中,假如要保护当地变量,不能不运用IIFE:
(function () { // open IIFE
var tmp = ···;
···
}()); // close IIFE
console.log(tmp); // ReferenceError
而在es6中,则只须要代码块和let
声明:
{ // open block
let tmp = ···;
···
} // close block
console.log(tmp); // ReferenceError
更多信息:防止IIFEs
4. 从字符串拼接到模板常量
在es6中,关于字符串插值和多行字符串,Javscript能获得其字面值。
4.1 字符串插值
在es5中,是将结果放在字符串中举行拼接:
function printCoord(x, y) {
console.log('('+x+', '+y+')');
}
es6中,经由过程字符串字面模板,能够在字符串中插进去变量值:
function printCoord(x, y) {
console.log(`(${x}, ${y})`);
}
4.2 多行字符串
模板字面量也能输出多行字符串。
比方,在es5中,输出多行字符串,得如许:
var HTML5_SKELETON =
'<!doctype html>\n' +
'<html>\n' +
'<head>\n' +
' <meta charset="UTF-8">\n' +
' <title></title>\n' +
'</head>\n' +
'<body>\n' +
'</body>\n' +
'</html>\n';
假如经由过程反斜线转义新行,代码看起来会惬意点(但照样要显现的增加新行):
var HTML5_SKELETON = '\
<!doctype html>\n\
<html>\n\
<head>\n\
<meta charset="UTF-8">\n\
<title></title>\n\
</head>\n\
<body>\n\
</body>\n\
</html>';
而es6得模板字面量能跨多行:
const HTML5_SKELETON = `
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
</body>
</html>`;
更多信息:字面量模板
5. 从函数表达式到箭头函数
在es5中,在函数表达式中必需警惕运用this
。在示例中,我建立了辅佐变量_this_
,以便于我能再line B处运用:
function UiComponent {
var _this = this; // (A)
var button = document.getElementById('myButton');
button.addEventListener('click', function () {
console.log('CLICK');
_this.handleClick(); // (B)
});
}
UiComponent.prototype.handleClick = function () {
···
};
在es6中,能够运用箭头函数,它不会影响this
:
class UiComponent {
constructor() {
let button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('CLICK');
this.handleClick(); // (A)
});
}
handleClick() {
···
}
}
关于只返回结果的短小回调函数,箭头函数是异常方便的。
在es5中,以下的回调是相对冗杂的:
var arr = [1, 2, 3];
var squares = arr.map(function (x) { return x * x });
在es6中,箭头函数会更简约:
let arr = [1, 2, 3];
let squares = arr.map(x => x * x);
在定义参数时,假如只要一个参数,括号是能够省略的。因此,(x) => x x and x => x x 都是许可的。
更多信息:箭头函数
6. 处置惩罚多个返回值
一些函数或要领能经由过程数组或对象返回多个值。在es5中,老是须要建立中心变量来接见返回值。es6中,能够运用解构。
6.1 经由过程数组返回多个值
exec()
返回类数组对象的捕捉组。es5中,当要接见捕捉组的值时,须要一个中心变量:
var matchObj =
/^(\d\d\d\d)-(\d\d)-(\d\d)$/
.exec('2999-12-31');
var year = matchObj[1];
var month = matchObj[2];
var day = matchObj[3];
es6中,解构使代码更简朴:
let [, year, month, day] =
/^(\d\d\d\d)-(\d\d)-(\d\d)$/
.exec('2999-12-31');
空得逗号示意跳过数组的第一个元素。
6.2 经由过程对象返回多个值
Object.getOwnPropertyDescriptor()
会返回一个属性描述符对象。
es5中,仍须要一个中心变量来接见你感兴趣的对象属性值:
var obj = { foo: 123 };
var propDesc = Object.getOwnPropertyDescriptor(obj, 'foo');
var writable = propDesc.writable;
var configurable = propDesc.configurable;
console.log(writable, configurable); // true true
es6,能够运用解构:
let obj = { foo: 123 };
let {writable, configurable} =
Object.getOwnPropertyDescriptor(obj, 'foo');
console.log(writable, configurable); // true true
{writable, configurable}
的值以下:
{ writable: writable, configurable: configurable }
更多信息:解构
7. 从for到forEach再到for-of
先说es6,平常会如许迭代数组:
var arr = ['a', 'b', 'c'];
for (var i=0; i<arr.length; i++) {
var elem = arr[i];
console.log(elem);
}
也能够运用Array
的forEach
:
arr.forEach(function (elem) {
console.log(elem);
});
for
轮回的长处是能够中缀,forEach
的长处是简约。
而es6的for-of
轮回则连系了二者的长处:
let arr = ['a', 'b', 'c'];
for (let elem of arr) {
console.log(elem);
}
for-of
轮回也能经由过程数组的entries
要领和解构返回数组的索引和对应的值:
for (let [index, elem] of arr.entries()) {
console.log(index+'. '+elem);
}
更多信息:for-of轮回
8. 参数默许值
es5中,指定参数默许值得如许:
function foo(x, y) {
x = x || 0;
y = y || 0;
···
}
es6有个更简约的语法:
function foo(x=0, y=0) {
···
}
es6语法还一个长处:参数默许值只能被undefined
触发,而在es5中,则会被任何z为false
的值触发。
更多信息:参数默许值
9. 定名参数
在Javascript中,定名参数的广泛体式格局是对象字面量:
selectEntries({ start: 0, end: -1 });
其等价完成:
function selectEntries(options) {
var start = options.start || 0;
var end = options.end || -1;
var step = options.step || 1;
···
}
es6中,解构是语法更简朴:
function selectEntries({ start=0, end=-1, step=1 }) {
···
}
9.1 使参数可选
es5中使参数可选的做法是如许的:
function selectEntries(options) {
options = options || {}; // (A)
var start = options.start || 0;
var end = options.end || -1;
var step = options.step || 1;
···
}
es6中,能够指定{}
为参数的默许值:
function selectEntries({ start=0, end=-1, step=1 } = {}) {
···
}
更多信息::模仿定名参数
10. 从arguments到参数扩大
es5中,若想让要领或函数接收恣意个数的参数,就必需运用指定的arguments
变量:
function logAllArguments() {
for (var i=0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
es6中,能够运用...
操纵到达同样地结果:
function logAllArguments(...args) {
for (let arg of args) {
console.log(arg);
}
}
另有更nice的语法:
function format(pattern, ...args) {
···
}
而es5中的处置惩罚则相对愚笨:
function format() {
var pattern = arguments[0];
var args = arguments.slice(1);
···
}
更多信息:Rest parameters
11. 从apply到漫衍操纵符(…)
es5中,apply()
会将数组转会成参数,es6中运用漫衍操纵符到达同样地目标。
11.1 Math.max()
es5–>apply()
:
> Math.max.apply(null, [-1, 5, 11, 3])
11
es6–>spread operator:
> Math.max(...[-1, 5, 11, 3])
11
11.2 Array.prototype.push()
es5–>apply()
:
var arr1 = ['a', 'b'];
var arr2 = ['c', 'd'];
arr1.push.apply(arr1, arr2);
// arr1 is now ['a', 'b', 'c', 'd']
es6–>spread operator:
let arr1 = ['a', 'b'];
let arr2 = ['c', 'd'];
arr1.push(...arr2);
// arr1 is now ['a', 'b', 'c', 'd']
更多信息:spread operator
12. 从concat()到(…)
ES5 – concat():
var arr1 = ['a', 'b'];
var arr2 = ['c'];
var arr3 = ['d', 'e'];
console.log(arr1.concat(arr2, arr3));
// [ 'a', 'b', 'c', 'd', 'e' ]
ES6 – spread operator:
let arr1 = ['a', 'b'];
let arr2 = ['c'];
let arr3 = ['d', 'e'];
console.log([...arr1, ...arr2, ...arr3]);
// [ 'a', 'b', 'c', 'd', 'e' ]
更多信息:spread operator
13. 从组织器到类
关于组织器语法,es6的类则更轻便。
13.1 基本类
es5中完成一个基本类以下:
function Person(name) {
this.name = name;
}
Person.prototype.describe = function () {
return 'Person called '+this.name;
};
es6中,类供应了更简约的语法:
class Person {
constructor(name) {
this.name = name;
}
describe() {
return 'Person called '+this.name;
}
}
13.2 派生类
es5完成了类的派生,下面是完成派生类的一种范例要领:
function Employee(name, title) {
Person.call(this, name); // super(name)
this.title = title;
}
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.describe = function () {
return Person.prototype.describe.call(this) // super.describe()
+ ' (' + this.title + ')';
};
es6内置了类派生语法,要借助extends
关键字:
class Employee extends Person {
constructor(name, title) {
super(name);
this.title = title;
}
describe() {
return super.describe() + ' (' + this.title + ')';
}
}
更多信息:类
14. 从自定义error到Error派生
跟上面有点相似。es5中自定义error:
function MyError() {
// Use Error as a function
var superInstance = Error.apply(null, arguments);
copyOwnPropertiesFrom(this, superInstance);
}
MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;
es6经由过程派生完成:
class MyError extends Error {
}
更多自信心:Subclassing built-in constructors
15. 从对象字面量的函数表达式和要领定义
语法上的差异。es5完成:
var obj = {
foo: function () {
···
},
bar: function () {
this.foo();
}, // trailing comma is legal in ES5
}
es6:
let obj = {
foo() {
···
},
bar() {
this.foo();
},
}
更多信息:要领定义
16. 从对象到图
es5中应用对象来完成图的数据结构,须要将对象的prototype
指向null
,并保证__proto__
上没有对应的键。
var dict = Object.create(null);
function countWords(word) {
var escapedWord = escapeKey(word);
if (escapedWord in dict) {
dict[escapedWord]++;
} else {
dict[escapedWord] = 1;
}
}
function escapeKey(key) {
if (key.indexOf('__proto__') === 0) {
return key+'%';
} else {
return key;
}
}
es6则内置了Map数据结构;
let map = new Map();
function countWords(word) {
let count = map.get(word) || 0;
map.set(word, count + 1);
}
更多信息:Maps and Sets
17. 从CommonJS模块到es6 模块
es5中,模块体系是基于AMD或CommocJS语法。es6内置了模块语法,但并没有获得Javascript引擎优越支撑。
17.1 多个导出
在CommonJS中,能够如许完成:
//------ lib.js ------
var sqrt = Math.sqrt;
function square(x) {
return x * x;
}
function diag(x, y) {
return sqrt(square(x) + square(y));
}
module.exports = {
sqrt: sqrt,
square: square,
diag: diag,
};
//------ main1.js ------
var square = require('lib').square;
var diag = require('lib').diag;
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5
es6的语法是酱紫的:
//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diag(x, y) {
return sqrt(square(x) + square(y));
}
//------ main1.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5
或许作为一个对象导入:
//------ main2.js ------
import * as lib from 'lib'; // (A)
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5
17.2 单个导出
Node.js继续了CommonJS的语法,能从模块导出单个值:
//------ myFunc.js ------
module.exports = function () { ··· };
//------ main1.js ------
var myFunc = require('myFunc');
myFunc();
es6经由过程export default
完成:
//------ myFunc.js ------
export default function () { ··· } // no semicolon!
//------ main1.js ------
import myFunc from 'myFunc';
myFunc();
更多信息:Modules
相干文章:ECMAScript 6新特征引见
译文出处:Getting started with ECMAScript 6