转载

你好 ES2015

原文: Say Hello To ES2015

笔记:涂鸦码龙

介绍 ES2015

ES2015 是新版的 JavaScript,Node.js 已经完全支持,浏览器端可以用 Babel 库编译。

运行本文的示例代码,可以用 JSBin 环境 ,也可以结合原文中的测试题检测学习效果。

使用 let 和 const

ES2015 可以用 constlet 替换 var ,它们定义了块级作用域变量。

示例代码:

for (var lc=0; lc < 10; lc++) {
let value = lc;
}

console.log(value); //抛出错误

变量 value 只能在 for 循环中使用。

constlet 很像,但是它定义的变量值无法改变。

示例代码:

const user = {name: "Arunoda"};
user.name = "Susiripala";
console.log(user.name);

改变的是变量 user 内部的属性,并没有改变 user 本身。

许多人更喜欢用 const 代替 let

使用箭头函数整理你的代码

熟悉的方式:

const numbers = [10, 20, 30, 50];
const multiplyBy10 = numbers.map(function(a) {
return a * 10;
});
console.log(multiplyBy10);

使用箭头函数以后:

const numbers = [10, 20, 30, 50];
const multiplyBy10 = numbers.map(a => a * 10);
console.log(multiplyBy10);

如果方法接受不止1个参数,可以这么写:

const numbers = [10, 20, 30, 50];
const multiplyByIndex = numbers.map((a, i) => a * i);
console.log(multiplyByIndex);

箭头函数返回一个对象的话,需要加圆括号:

const numbers = [10, 20, 30, 50];
const multiplyBy10 = numbers.map(a => ({res: a * 10}));
console.log(multiplyBy10);

不再使用“self=this”

以前的代码:

function Clock() {
this.currentTime = new Date();
}

Clock.prototype.start = function() {
var self = this;
setInterval(function() {
self.currentTime = new Date();
}, 1000);
}

可以用箭头函数代替 self=this

function Clock() {
this.currentTime = new Date();
}

Clock.prototype.start = function() {
setInterval(() => {
this.currentTime = new Date();
}, 1000);
}

setInterval 里面用了箭头函数,它携带了 start 方法的上下文 (this)

使用箭头函数要多加小心,并不是随处可用的,箭头函数会携带函数定义时的上下文。

正如这个例子: https://jsbin.com/zuseqap/edit?js,console

改良的对象字面量

在对象里面定义一个方法,可以这么写:

const user = {
getName() {
return 'Arunoda';
}
}

console.log(user.getName());

不必每次都写 function 关键字。

这是最酷的特性,你会喜欢的:

const name = 'Arunoda';
const age = 80;

const user = {name, age};

瞅瞅多简单,并不用这么啰嗦:

const name = 'Arunoda';
const age = 80;

const user = {
name: name,
age: age
};

解构对象

很容易地提取 user 对象的 nameage 字段:

const user = {
name: 'Arunoda',
age: 80,
city: 'Colombo'
};

const {name, age} = user;
console.log(name, age);

对于函数相当有用,上代码:

function printName({name}) {
console.log('Name is: ' + name);
}

const user = {
name: 'Arunoda',
age: 80,
city: 'Colombo'
};
printName(user);

不仅简化了代码,而且可以自描述。看到函数第一行时,我们便会明白使用传入对象的哪个字段。

可以定义传入对象的默认值。

function printUser({name, age = 20}) {
console.log('Name is: ' + name + ' Age: ' + age);
}

像传入对象一样,同样可以从传入的数组中解构值:

function printUser([name, age = 20]) {
console.log('Name is: ' + name + ' Age: ' + age);
}

printUser(["Arunoda", 80]);

前所未见的方式传递(spread)数组

以前的代码:

function sum(a, b) {
return a + b;
}

function sumAndLog(a, b) {
var result = sum(a, b);
console.log('Result is: ' + result);
return result;
}

sumAndLog(10, 20);

ES2015 代码:

function sum(a, b) {
return a + b;
}

function sumAndLog(...args) {
const result = sum(...args);
console.log('Result is: ' + result);
return result;
}

sumAndLog(10, 20);

sumAndLog 方法中使用 spread 操作符( ... ),可以很简单地把所有参数存入 args 变量,然后再用 spread 操作符把 args 传入 sum 方法。

再看以下例子:

function printTeam(leader, ...others) {
console.log('Leader: ' + leader + ' - Others: ' + others);
}

printTeam('Arunoda', 'John', 'Singh');
//输出结果:"Leader: Arunoda - Others: John,Singh"

克隆、合并对象

以往都是用 underscore 或者 lodash ,克隆、合并对象:

var user = {name: "Arunoda"};
var newUser = _.clone(user);
var withAge = _.extend(user, {age: 20});
var newUserVersion = _.defaults({age: 80}, user);

console.log(newUser, withAge, newUserVersion);

ES2015 不需要任何工具库,轻松实现以上功能。

const user = {name: "Arunoda"};
const newUser = {...user};
const withAge = {...user, age: 20};
const newUserVersion = {age: 80, ...user};

console.log(newUser, withAge, newUserVersion);

看以下例子:

const user = {
name: 'Arunoda',
emails: ['hello@arunoda.io']
};

const newUser = {...user};
newUser.emails.push('mail@arunoda.io');

console.log(user.emails);
//输出结果:["hello@arunoda.io", "mail@arunoda.io"]

尽管我们克隆了对象,但不是深度克隆,只克隆了顶层字段,emails 数组字段使用的仍是同一个。

往数组里添加元素

跟对象类似,我们同样可以克隆数组:

const marks = [10, 20, 30];
const newMarks = [...marks, 40];

console.log(marks, newMarks);

JavaScript 不变性(Immutability)

这些日子,JavaScript 也兴起函数式编程的概念。因此,我们可以尝试写写纯函数。

纯函数:一个函数接收一些值,并且返回一些值,但是通过参数接收到的值不会被改变。 同样的输入总是返回同样的值。random() 就不是一个纯函数,任何可以修改全局状态的函数都不能称之为纯。

spread 操作符可以轻松实现。

用于对象:

function addMarks(user, marks) {
return {
...user,
marks
};
}

const user = {username: 'arunoda'};
const userWithMarks = addMarks(user, 80);

console.log(user, userWithMarks);

用于数组:

function addUser(users, username) {
const user = {username};
return [
...users,
user
];
}

const user = {username: 'arunoda'};
const users = [user];
const newUsers = addUser(users, 'john');

console.log(users, newUsers);

以 Python 方式合并字符串

合并字符串通常很烦,可以用 + 操作符,或者类似 underscore 的模板。

ES2015 的模板字符串,非常简单,看例子:

const name = "Arunoda";
const welcome = `Hello ${name}, Good Morning!`;

console.log(welcome);

注意 “`” 的使用。

多行字符串

既然支持模板字符串,多行字符串也不在话下:

const message = `
# Title

This is a multi line string as markdown.
It's pretty nice.
`;

console.log(message);

没有模板字符串的话,是这个样子的:

var message = "/n  # Title/n/n  This is a multi line string as markdown./n  It's pretty nice./n";
console.log(message);

像 Java 一样写 Class

JavaScript 并不是真正的面向对象语言,但是可以用函数和原型模拟类。ES2015 可以写真正原生的类了。

class Vehicle {
constructor(type, number) {
this.type = type;
this.number = number;
}

display() {
return `Number: ${this.number}`;
}
}

const v1 = new Vehicle('Car', 'GH-2343');
console.log(v1.display());

继承一个类:

class Vehicle {
constructor(type, number) {
this.type = type;
this.number = number;
}

display() {
return `Number: ${this.number}`;
}
}

class Car extends Vehicle {
constructor(number) {
super('Car', number);
}

display() {
const value = super.display();
return `Car ${value}`;
}
}

const v1 = new Car('GH-2343');
console.log(v1.display());

小汽车继承了车辆:

  • 在 Car constructor 内部调用了 super constructor (Vehicle 的 constructor)。
  • Car 的 display() 方法内部,调用了 super.display() 。这里展示了子类如何继承方法。
class Vehicle {
constructor(type, number) {
this.type = type;
this.number = number;
}

display() {
return `Number: ${this.number}`;
}
}

class Car extends Vehicle {
display() {
const value = super.display();
return `Car ${value}`;
}
}

const v1 = new Car('GH-2343');
console.log(v1.display());

Car 类没有实现 constructor 的话,它会用 Vehicle 的 constructor 。

ES2015 模块系统

ES2015 的模块系统很像 CommonJS 模块系统(或者 Node.js 的模块系统),但是有一点主要的区别:

所有的模块导入应该是静态的,无法在运行时导入模块。编译时间应该完成导入(或者最好在解释 JavaScript 期间完成)。以下代码在 ES2015 模块里无法使用:

let router;

if (typeof window === 'function') {
router = import './client-router';
} else {
router = import './server-router';
}

命名导出函数

定义一个简单的导出函数 sum

export function sum(a, b) {
return a + b;
}

然后导入它:

import {sum} from './lib/math';

const total = sum(10, 20);
console.log(total);

导入多个函数:

import {sum, multiply}

像函数一样,可以导出任何类型的变量,包括类。

export const BASE = 10;
export let name = 'Arunoda';
export class Vehicle {};

默认导出

有时需要导出一个独立的模块,叫做默认导出。

export default class {
constructor(type, number) {
this.type = type;
this.number = number;
}

display() {
return `Number: ${this.number}`;
}
}

可以这么导入:

import Vehicle from './lib/vehicle';

const v1 = new Vehicle('Car', 'GH-3355');
console.log(v1.display());

如果再导出一个 print 函数,这么写:

import Vehicle, {print} from './lib/vehicle';

这便是我们如何在同一模块导入 “默认导出” 和 “命名导出” 的方式。

重命名导入,一次导入所有命名导出,以及更多知识,参见 MDN 文档 。

在实际的项目中使用 ES2015?

至今还没有浏览器完全实现 ES2015 的所有规范。因此,无法直接在浏览器里使用 ES2015 。

那么我们该怎么做?

欢迎来到 transpiling 的世界。

现在可以按 ES2015 写代码,然后使用一个工具把它转换成 ES5,最有名的一个 transpiler 便是 Babel 。

设置 Babel 并没那么简单,需要一定的经验,这是一些新手包,拿去用吧。

  • 在 Node.js 中使用 node-base
  • 在 NPM 模块中使用 npm-base
  • 在 React app 中使用 react-hot-boilerplate

此外,可以使用 Meteor ,默认支持 ES2015 了。

深入研究 ES2015

深入研究,可以参考以下链接:

  • Babel’s Learn ES2015 Guide
  • Massive List of ES2015 Learning Resources
原文  http://jinlong.github.io/2016/04/09/Say-Hello-To-ES2015/
正文到此结束
Loading...