Skip to content

Instantly share code, notes, and snippets.

@mcattx
Created February 8, 2017 07:44
Show Gist options
  • Save mcattx/172f8bafffc6d6569a233655bb9e8f86 to your computer and use it in GitHub Desktop.
Save mcattx/172f8bafffc6d6569a233655bb9e8f86 to your computer and use it in GitHub Desktop.
a simple introduction for es2015

#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

ES2015 在现代浏览器(Chrome Firefox IE10+)的实现程度

浏览器实现程度

  • Chrome 54+实现了ES2015 标准的 97%
  • Firefox 49实现了ES2015 标准的 92%
  • IE 11实现了ES2015 标准的 11%
  • Edge 13实现了ES2015 标准的 83%

浏览器实现程度传送门

ES2015 在 Node 的 V8 引擎实现程度

v8 实现程度

  • v0.10.48实现了ES2015 标准的 11%
  • v4.3.2实现了ES2015 标准的 57%
  • v6.8.1实现了ES2015 标准的 99%

v8 实现程度传送门

##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
  • ...

Block Bindings

// 表面上是这样:
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 —— 使用 letconst代替 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 ,只要注意几点就行了。

Arrow function

在 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

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]

Generator

我们先来了解一下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);     // 3

Generator可以用来解决异步编程的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 是一个对象,从它可以获取异步操作的消息。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

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 && Reflect

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment