#ES2015 101
##Why we need ES2015?
JavaScript - Dom - Bom = ECMAScript
我们熟知的 ECMAScript 5 已经在全部的现代浏览器实现了,当然 IE10+才算是现代浏览器。
ECMAScript 2015 —— 很多人称为 ES6 ,因为它是 ECMAScript 标准的第六版修订。负责指定 ECMAScript 标准的委员会 TC39 决定把新标准的制度改为一年一次,所以现在有了 ES2016 (ES7) 。
ES2015 是 JavaScript 语言的实现标准,是未来的方向。 以前还有 IE6 的时候,我们用上了 ES5; 尽管浏览器还没完全支持,但是也应该学起来。通过使用 Babel 和 Traceur 这些编译器,我们可以把 ES2015+ 编译成 ES5 从而在现代的浏览器用起来。当然,因为我司要兼容 IE7+ ,所以目前的用途只能用在移动端的浏览器开发。
##The degree of ES2015 implementation
Chrome 54+实现了ES2015 标准的 97%Firefox 49实现了ES2015 标准的 92%IE 11实现了ES2015 标准的 11%Edge 13实现了ES2015 标准的 83%
v0.10.48实现了ES2015 标准的 11%v4.3.2实现了ES2015 标准的 57%v6.8.1实现了ES2015 标准的 99%
##ES2015 Complier: Babel/Traceur
Babel 已经完全实现 ES2015 的所有标准,只需要使用 babel-preset-es2015
假如你要使用所有最新的标准包括 ES2016 和 ESNext,那就使用 babel-preset-latest
Babel 只支持预编译,但是支持多种构建工具和开发环境。
Traceur 是 Google 出的编译器,但是用的人比较少。但是支持 <script> 标签引入
##The new things in ES2015
- Block Bindings
- Arrow function
- Destructuring
- Generator
- Promise
- Set && Map
- Proxy && Reflect
- ...
// 表面上是这样:
function getValue(condition) {
if (condition) {
var value = "blue";
// other code
return value;
} else {
// value exists here with a value of undefined
return null;
}
// value exists here with a value of undefined
}
// 实际上
function getValue(condition) {
var value;
if (condition) {
value = "blue";
// other code
return value;
} else {
return null;
}
}ES5 并没有 Block Bindings (块级作用域),为了避免新手开发遇到这个坑。 ES2015 增加了Block Bindings —— 使用 let和const代替 var
使用后:
function getValue(condition) {
if (condition) {
let value = "blue";
// other code
return value;
} else {
// value doesn't exist here
return null;
}
// value doesn't exist here
}你可以用 let 和 const 完全代替你的 var ,只要注意几点就行了。
在 Javascript 里有个臭名昭著的this坑,为了填这个坑,我看过很多的写法:
var self = this;
var _this = this;
var that = this;
var thiz = this;
var she = this;
var me = this;
...现在有了箭头函数,妈妈再也不用担心我的this了。
以下是语法演示:
// single argument
var reflect = value => value;
// effectively equivalent to:
var reflect = function(value) {
return value;
};
// multi arguments
var sum = (num1, num2) => num1 + num2;
// effectively equivalent to:
var sum = function(num1, num2) {
return num1 + num2;
};立即执行函数
// IIFE original
let person = function(name) {
return {
getName: function() {
return name;
}
}
}('Nicholas');
console.log(person.getName()); // "Nicholas"
// IIFE
let person = ((name) => {
return {
getName: function() {
return name;
}
};
})("Nicholas");
console.log(person.getName()); // "Nicholas"常见的 this坑如下:
var PageHandler = {
id: "123456",
init: function() {
document.addEventListener("click", function(event) {
this.doSomething(event.type); // error
}, false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};这个this的坑本质上是Javascript设计上的缺陷,毕竟是十天创造出来的语言。
使用 ES5 的bind解决
var PageHandler = {
id: "123456",
init: function() {
document.addEventListener("click", (function(event) {
this.doSomething(event.type); // no error
}).bind(this), false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};使用 ES2015 的Arrow function 解决:
var PageHandler = {
id: "123456",
init: function() {
document.addEventListener("click",
event => this.doSomething(event.type), false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};箭头函数这种本质上是语法糖。
注意事项:
箭头函数不能当构造函数,假如你像以下这样做:
var MyType = () => {},
object = new MyType();
// error - you can't use arrow functions.
// You will get an error: Uncaught TypeError: MyType is not a constructor箭头函数里不能使用arguments,但是你可以使用rest
Destructuring 的意思是解构,所谓的解构:
let options = {
repeat: true,
save: false
};
// extract data from the object
let repeat = options.repeat,
save = options.save;Object 类型 的 Destructuring
let node = {
type: "Identifier",
name: "foo"
};
let { type, name } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
// 解构 的本质其实也是一种语法糖,只是 ES2015 内部帮我们实现了同名的 key 而已解构赋值:
let node = {
type: "Identifier",
name: "foo"
},
type = "Literal",
name = 5;
// assign different values using destructuring
// 一定要同名
({ type, name } = node);
console.log(type); // "Identifier"
console.log(name); // "foo"
// 假如解构的时候增加一个新增字段 value,那它的值是 undefined
let { type, name, value } = node;
console.log(value); // undefined
// 你也可以指定一个值
let { type, name, value = true } = node;
console.log(value); // true
// 解构的时候可以指定别名
let { type: localType, name: localName } = node;
console.log(localType); // "Identifier"
console.log(name); // "foo"
console.log(type); // ReferenceError :type is not defined 报错 !Array 类型 的 Destructuring
let colors = [ "red", "green", "blue" ];
let [ firstColor, secondColor ] = colors;
let [ , , thirdColor ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
console.log(thirdColor); // "blue"经典的值交换
// Swapping variables in ECMAScript 5
let a = 1,
b = 2,
tmp;
tmp = a;
a = b;
b = tmp;
console.log(a); // 2
console.log(b); // 1
// Swapping variables in ECMAScript 6
let a = 1,
b = 2;
[ a, b ] = [ b, a ];
console.log(a); // 2
console.log(b); // 1使用场景
1. 从函数返回多个值
// 返回一个数组
function example() {
return [1, 2, 3];
}
var [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
var { foo, bar } = example();
2. 输入模块的指定方法
const { SourceMapConsumer, SourceNode } = require("source-map");
3. 遍历Map结构
var map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is hello
// second is world
4. 提取JSON数据
var jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]我们先来了解一下Iterators的概念:
// 假如我们需要逐个输出 colors 的值,我们可能写成这样
var colors = ["red", "green", "blue"];
for (var i = 0, len = colors.length; i < len; i++) {
console.log(colors[i]);
}
// 假如我们换个方式
function createIterator(items) {
var i = 0;
return {
next: function() {
var done = (i >= items.length);
var value = !done ? items[i++] : undefined;
return {
done: done,
value: value
};
}
};
}
var iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
// for all further calls
console.log(iterator.next()); // "{ value: undefined, done: true }"而Generator就是返回一个这样的一个Iterators。
// generator
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
// generators are called like regular functions but return an iterator
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3Generator可以用来解决异步编程的callback hell
//异步编程的常见解决方案
// 1. callback
let fs = require("fs");
fs.readFile("config.json", function(err, contents) {
if (err) {
throw err;
}
doSomethingWith(contents);
console.log("Done");
});
// 2. genarator
let fs = require("fs");
function readFile(filename) {
return function(callback) {
fs.readFile(filename, callback);
};
}
run(function*() {
let contents = yield readFile("config.json");
doSomethingWith(contents);
console.log("Done");
});
// 3.promise
let fs = require('fs');
function readFile(filename) {
return new Promise(function(resolve, reject) {
// trigger the asynchronous operation
fs.readFile(filename, {encoding: "utf8"}, function(err, contents) {
if(err) {
reject(err);
return;
}
// the read succeeded
resolve(contents);
})
})
}
let promise = readFile("example.txt");
// listen for both fulfillment and rejection
promise.then(function(contents) {
// fulfillment
console.log(contents);
}, function(err) {
// rejection
console.error(err.message);
})语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 只有三个状态
- pending
- resolve
- reject
Promise 一开始是处于pending状态,事件执行后只会转为Fulfilled或者是Rejected
let promise = new Promise(resolve, reject);
// promise.then
promise.then(resolvedCallback, rejectedCallback)
// 展开 ↓
promise.then(function(value) {
// fulfillment
console.log(value);
}, function(err) {
// rejection
console.error(err.message);
});
promise.then(resolvedCallback)
promise.then(null, rejectedCallback)
promise.then(null, rejectedCallback)
// is the same as:
promise.catch(rejectedCallback);
// 展开 ↓
promise.catch(function(err) {
// rejection
console.error(err.message);
});Set && Map 是 ES2015 新增的两种数据结构。
Set 是类似于数组,但是成员的值都是唯一的,没有重复的值。
var s = new Set();
[2, 3, 5, 4, 5, 2, 2].map(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4
console.log(s.size) // 4常用方法:
Set.prototype.size:返回Set实例的成员总数。
add(value):添加某个值,返回Set结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值。
keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员注意事项:
let s1 = new Set(),
ks1 = 0,
ks2 = 0;
s1.add(ks1)
s1.add(ks2)
console.log(s1.size) // 1
// 基本类型和 object 这种引用类型不一样!
let s2 = new Set(),
ks3 = {},
ks4 = {};
s2.add(ks3)
s2.add(ks4)
console.log(s2.size) // 2
s1.add(0)
console.log(s1.size)Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
一个简单的例子:
let target = {};
let proxy = new Proxy(target, {});
proxy.name = "proxy";
console.log(proxy.name); // "proxy"
console.log(target.name); // "proxy"
target.name = "target";
console.log(proxy.name); // "target"
console.log(target.name); // "target"假如我们想要一个 object 的所有属性都必须是 Number类型的。
let target = {
name: 'target'
};
let proxy = new Proxy(target, {
set(trapTarget, key, value, receiver) {
// ignore existing properties so as not to affect them
if(!trapTarget.hasOwnProperty(key)) {
if(isNaN(value)) {
throw new TypeError('Property must be number')
}
// add the property
return Reflect.set(trapTarget, key, value, receiver)
}
}
})
// adding a new property
proxy.count = 1;
console.log(proxy.count); // 1
console.log(target.count); // 1
// you can assign to name because it exists on target already
proxy.name = "proxy";
console.log(proxy.name); // "proxy"
console.log(target.name); // "proxy"
// throws an error
proxy.anotherName = "proxy";上面 Proxy 构造函数的第二个参数意思:
trapTarget - the object that will receive the property (the proxy’s target)
key - the property key (string or symbol) to write to
value - the value being written to the property
receiver - the object on which the operation took place (usually the proxy)除了上述的还有 Symbol Class Module

