時間 | ECMA | JS | 解釋 |
---|---|---|---|
1996.11 | ES 1.0 | JS穩定 | Netscape將JS提交給ECMA組織,ES正式出現 |
1998.06 | ES 2.0 | ES2正式發佈 | |
1999.12 | ES 3.0 | ES3被普遍支持 | |
2007.10 | ES 4.0 | ES4過於激進,被廢了 | |
2008.07 | ES 3.1 | 4.0退化爲嚴重縮水版的3.1<br/>由於吵得太厲害,因此ES 3.1代號爲Harmony(和諧) | |
2009.12 | ES 5.0 | ES 5.0正式發佈<br/>同時公佈了JavaScript.next也就是後來的ES 6.0 | |
2011.06 | ES 5.1 | ES 5.1成爲了ISO國際標準 | |
2013.03 | ES 6.0 | ES 6.0草案定稿 | |
2013.12 | ES 6.0 | ES 6.0草案發布 | |
2015.06 | ES 6.0 | ES 6.0預計發佈正式版<br/>JavaScript.next開始指向ES 7.0 |
Stage 0 - Strawman(展現階段) Stage 1 - Proposal(徵求意見階段) Stage 2 - Draft(草案階段) Stage 3 - Candidate(候選人階段) Stage 4 - Finished(定案階段)
暫時性死區(temporal dead zone,簡稱 TDZ):在代碼塊內,聲明變量以前,該變量都是不可用的。html
區別:es6
ES6 容許按照必定模式,從數組和對象中提取值,對變量進行賦值,這被稱爲解構(Destructuring)。
適用於數組(這裏泛指具備Iterator接口的數據結構)、對象、字符串、數值和布爾值,可設置默認值。ajax
//數組 let [x, y = 'b', z='c'] = ['a', undefined, null];// x='a', y='b' z=null //對象 let { bar, foo } = { foo: "aaa", bar: "bbb" };//foo = "aaa"; bar = "bbb" //字符串,先轉爲數組 const [a, b, length : len] = 'ho';//a="h";b= "o";len = 2 //數值與布爾值,先轉爲對象 let {toString: s} = 123;//s = Number.prototype.toString let {toString: s} = true;//s = Boolean.prototype.toString
includes(String)
:是否找到參數字符串。startsWith(String)
:是否以參數字符串開頭。endsWith(String)
:是否以參數字符串結尾。let s = 'Hello world!'; s.includes('o') // true //都可接受第二個位置參數
repeat(Number)
'x'.repeat(3) // "xxx"
$('#result').append(` There are <b>${basket.count}</b> items in your basket, <em>${basket.onSale}</em> are on sale! `); //trim $('#list').html(` <ul> <li>first</li> <li>second</li> </ul> `.trim());
Number.isFinite()
-只對數值返回trueNumber.isNaN()
-只對NaN返回trueNumber.isInteger()
-只對整數返回trueNumber.isSafeInteger()
-只對安全整數(-2^53到2^53,不含端點)返回trueNumber.MAX_SAFE_INTEGER
,Number.MIN_SAFE_INTEGER
安全整數的上下限常量Number.isInteger(25) // true Number.isInteger(25.0) // true Number.isInteger(25.1) // false
Math對象擴展:http://es6.ruanyifeng.com/#do...編程
function log(x, y = 'World') {}
(function (a, b = 1, c) {}).length // 1
function add(...values) {} add(2, 5, 3)
function f() {} //或者 var f = function () {}; f.name // "f"
let getTempItem = id => ({ id: id, name: "Temp" });
const full = ({ first, last }) => first + ' ' + last; const pipeline = (...funcs) => val => funcs.reduce((a, b) => b(a), val);
注意:json
用於數組賦值須放在參數的最後一位。數組
console.log(1, ...[2, 3, 4], 5) // 1 2 3 4 5 //複製數組 const a1 = [1, 2]; const a2 = [...a1];// 寫法一 const [...a2] = a1;// 寫法二 //合併數組 const a2 = [3, 4]; [...a1, ...a2] //[1,2,3,4] //配合解構賦值 const [first, ...rest] = [1, 2, 3, 4, 5];// first= 1;rest= [2, 3, 4, 5] //字符串 [...'hello'] // [ "h", "e", "l", "l", "o" ]
Array.from()
:將數組對象(有length屬性)或可遍歷的對象(包括 ES6 新增的數據結構 Set 和 Map)轉爲真正的數組,接受第二個參數(對每一個元素進行處理,處理值返回數組)。let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
Array.of()
:將一組值轉換爲數組。Array.of(3, 11, 8) // [3,11,8]
copyWithin(target[必需,替換開始位置,負值爲倒數], start = 0[可選,讀取開始位置,負值爲倒數], end = this.length[可選,讀取結束位置,負值爲倒數])
:在當前數組內部,將指定位置的成員複製到其餘位置(會覆蓋原有成員),而後返回當前數組。[1, 2, 3, 4, 5].copyWithin(0, 3) // [4, 5, 3, 4, 5]
find(fn)
和 findIndex(fn)
:找出第一個符合條件的數組成員。若是無符合條件的成員,返回undefined。//find方法的回調函數能夠接受三個參數,依次爲當前的值、當前的位置和原數組 [1, 5, 10, 15].find(function(value, index, arr) { return value > 9; }) // 10
fill(text[必需,填充內容],start = 0[可選,開始位置,負值爲倒數], end = this.length[可選,結束位置,負值爲倒數])
: 使用給定值,填充一個數組。['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']
includes(text[必需,內容],start = 0[可選,搜索開始位置,負值爲倒數], end = this.length[可選,搜索結束位置,負值爲倒數])
:搜索數組是否包含給定的值。[1, 2, 3].includes(3, -1); // true
const foo = 'bar'; const baz = { foo, method() { return "Hello!"; } }
let propKey = 'foo'; let obj = { [propKey]: true, ['a' + 'bc']: 123 }
Object.is()
: 判斷兩值是否相等//和===的區別 +0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // true
Object.assign(target[目標對象], source1, source2,...)
: 將源對象全部可枚舉屬性複製合併到目標對象。const target = { a: 1, b: 1 }; const source1 = { b: 2, c: 2 }; const source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
集合:集合是由一組無序且惟一(即不能重複)的項組成的,能夠想象成集合是一個既沒有重複元素,也沒有順序概念的數組promise
ES6 提供了新的數據結構 Set。它相似於數組,可是成員的值都是惟一的,沒有重複的值。
Set 自己是一個構造函數,用來生成 Set 數據結構。瀏覽器
const s = new Set(); [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x)); for (let i of s) { console.log(i); } // 2 3 5 4 // 去除數組的重複成員 const set = new Set([1, 2, 3, 4, 4]); [...set] // [1, 2, 3, 4]
向 Set 加入值的時候,不會發生類型轉換。安全
Set 結構的實例有如下屬性。服務器
s.add(1).add(2).add(2); // 注意2被加入了兩次 s.size // 2 s.has(1) // true s.has(2) // true s.has(3) // false s.delete(2); s.has(2) // false
Set 結構的實例有四個遍歷方法,能夠用於遍歷成員。
須要特別指出的是,Set的遍歷順序就是插入順序。這個特性有時很是有用,好比使用 Set 保存一個回調函數列表,調用時就能保證按照添加順序調用。
let set = new Set(['red', 'green', 'blue']); for (let item of set.keys()) { console.log(item); } // red // green // blue for (let item of set.values()) { console.log(item); } // red // green // blue for (let item of set.entries()) { console.log(item); } // ["red", "red"] // ["green", "green"] // ["blue", "blue"]
WeakSet 結構與 Set 相似,也是不重複的值的集合。可是,它與 Set 有兩個區別。
首先,WeakSet 的成員只能是對象,而不能是其餘類型的值;
WeakSet 中的對象都是弱引用,即垃圾回收機制不考慮 WeakSet 對該對象的引用,也就是說,若是其餘對象都再也不引用該對象,那麼垃圾回收機制會自動回收該對象所佔用的內存。
WeakSet 是一個構造函數,可使用new命令,建立 WeakSet 數據結構。
const a = [[1, 2], [3, 4]]; const ws = new WeakSet(a); // WeakSet {[1, 2], [3, 4]}
WeakSet 結構有如下三個方法。
相似對象,惟一的區別是key的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵。主要用於數據存儲
const map = new Map([ ['name', '張三'], ['title', 'Author'] ]); map.size // 2 map.has('name') // true map.get('name') // "張三" map.has('title') // true map.get('title') // "Author"
set方法設置鍵名key對應的鍵值爲value,而後返回整個 Map 結構。若是key已經有值,則鍵值會被更新,不然就新生成該鍵。
const m = new Map(); m.set('edition', 6) // 鍵是字符串 m.set(262, 'standard') // 鍵是數值 m.set(undefined, 'nah') // 鍵是 undefined //或者 let map = new Map() .set(1, 'a') .set(2, 'b') .set(3, 'c');
get方法讀取key對應的鍵值,若是找不到key,返回undefined。
has方法返回一個布爾值,表示某個鍵是否在當前 Map 對象之中
delete方法刪除某個鍵,返回true。若是刪除失敗,返回false。
clear方法清除全部成員,沒有返回值。
提供三個遍歷器生成函數和一個遍歷方法。Map 的遍歷順序就是插入順序
const myMap = new Map() .set(true, 7) .set({foo: 3}, ['abc']); [...myMap] // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
new Map([ [true, 7], [{foo: 3}, ['abc']] ]) // Map { // true => 7, // Object {foo: 3} => ['abc'] // }
function strMapToObj(strMap) { let obj = Object.create(null); for (let [k,v] of strMap) { obj[k] = v; } return obj; } const myMap = new Map() .set('yes', true) .set('no', false); strMapToObj(myMap) // { yes: true, no: false }
function objToStrMap(obj) { let strMap = new Map(); for (let k of Object.keys(obj)) { strMap.set(k, obj[k]); } return strMap; } objToStrMap({yes: true, no: false}) // Map {"yes" => true, "no" => false}
Map 轉爲 JSON 要區分兩種狀況。一種狀況是,Map 的鍵名都是字符串,這時能夠選擇轉爲對象 JSON。
function strMapToJson(strMap) { return JSON.stringify(strMapToObj(strMap)); } let myMap = new Map().set('yes', true).set('no', false); strMapToJson(myMap) // '{"yes":true,"no":false}'
另外一種狀況是,Map 的鍵名有非字符串,這時能夠選擇轉爲數組 JSON。
function mapToArrayJson(map) { return JSON.stringify([...map]); } let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']); mapToArrayJson(myMap) // '[[true,7],[{"foo":3},["abc"]]]'
JSON 轉爲 Map,正常狀況下,全部鍵名都是字符串。
function jsonToStrMap(jsonStr) { return objToStrMap(JSON.parse(jsonStr)); } jsonToStrMap('{"yes": true, "no": false}') // Map {'yes' => true, 'no' => false}
可是,有一種特殊狀況,整個 JSON 就是一個數組,且每一個數組成員自己,又是一個有兩個成員的數組。這時,它能夠一一對應地轉爲 Map。這每每是數組轉爲 JSON 的逆操做。
function jsonToMap(jsonStr) { return new Map(JSON.parse(jsonStr)); } jsonToMap('[[true,7],[{"foo":3},["abc"]]]') // Map {true => 7, Object {foo: 3} => ['abc']}
WeakMap結構與Map結構相似,也是用於生成鍵值對的集合。
區別:
集合又和字典有什麼區別呢:
Promise 是異步編程的一種解決方案,比傳統的解決方案(回調函數和事件),更合理和更強大。
特色:
const promise = new Promise(function(resolve, reject) { // ... some code if (/* 異步操做成功 */){ resolve(value); //將Promise對象的狀態從「未完成」變爲「成功」,並將異步操做的結果,做爲參數傳遞出去 } else { reject(error); //將Promise對象的狀態從「未完成」變爲「失敗」,並將異步操做報出的錯誤,做爲參數傳遞出去 } }); //調用 promise.then(value=> { // success }, error=> { //可選 // failure }); //或者 promise.then(value => { // success }).catch(error => { // failure });
Promise.all([p1, p2, ...])
: 將多個 Promise 包裝成一個新的 Promise ,狀態和值取決於裏面的實例(fulfilled:需均成功,返回包含每一個實例返回值的數組;rejected:至少一個失敗,返回第一個失敗的實例返回值)Promise.race([p1, p2, ...])
: 將多個 Promise 包裝成一個新的 Promise,狀態和參數均由裏面第一個改變的狀態的實例決定Promise.resolve()
:簡寫,將現有對象轉爲 狀態爲fulfilled的Promise 對象。Promise.resolve('foo') // 等價於 new Promise(resolve => resolve('foo'))
Promise.reject()
:簡寫,將現有對象轉爲 狀態爲rejected的Promise 對象。const p = Promise.reject('出錯了'); // 等同於 const p = new Promise((resolve, reject) => reject('出錯了'))
數據集合:主要是Array,Object,Map,Set。
遍歷器(Iterator)爲上述數據集合提供了統一的訪問機制。
const arr = ['red', 'green', 'blue']; for(let v of arr) { console.log(v); // red green blue }
var arr = ['a', 'b', 'c', 'd']; for (let a in arr) { console.log(a); // 0 1 2 3 } for (let a of arr) { console.log(a); // a b c d }
for (var n of fibonacci) { if (n > 1000) break; console.log(n); }
Generator 函數是 ES6 提供的一種異步編程解決方案。
Generator 函數是一個狀態機,封裝了多個內部狀態。
function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } var hw = helloWorldGenerator(); hw.next() // { value: 'hello', done: false } hw.next() // { value: 'world', done: false } hw.next() // { value: 'ending', done: true }
Generator 函數的調用方法與普通函數同樣,也是在函數名後面加上一對圓括號。不一樣的是,調用 Generator 函數後,該函數並不執行,必須調用next()纔會執行,返回的也不是函數運行結果,而是一個指向內部狀態的指針對象(yield)。
遍歷器對象的next方法的運行邏輯以下。
每次next()都會跑到yield丟東西出來的那個步驟
//輸入輸出:yield右邊爲輸出(next返回值的value,最後一次由return返回),左邊爲輸入(接受的是next的參數,第一次由函數參數傳入), function * input(){ let array = [], i = 4; while(i) { array.push(yield array); i --; } } var gen = input(); console.log(gen.next("西")) // { value: [], done: false } console.log(gen.next("部")) // { value: [ '部' ], done: false } console.log(gen.next("世")) // { value: [ '部', '世' ], done: false } console.log(gen.next("界")) // { value: [ '部', '世', '界' ], done: false }
yield表達式自己沒有返回值,或者說老是返回undefined。next方法能夠帶一個參數,該參數就會被看成上一個yield表達式的返回值。
for...of循環能夠自動遍歷 Generator 函數時生成的Iterator對象,且此時再也不須要調用next方法。
function* foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; } for (let v of foo()) { console.log(v); } // 1 2 3 4 5
next()、throw()、return()這三個方法本質上是同一件事,能夠放在一塊兒理解。它們的做用都是讓 Generator 函數恢復執行,而且使用不一樣的語句替換yield表達式。
const g = function* (x, y) { let result = yield x + y; return result; }; const gen = g(1, 2); gen.next(); // Object {value: 3, done: false} gen.next(1); // Object {value: 1, done: true} // 至關於將 let result = yield x + y // 替換成 let result = 1;
gen.throw(new Error('出錯了')); // Uncaught Error: 出錯了 // 至關於將 let result = yield x + y // 替換成 let result = throw(new Error('出錯了'));
gen.return(2); // Object {value: 2, done: true} // 至關於將 let result = yield x + y // 替換成 let result = return 2;
在 Generator 函數內部,調用另外一個 Generator 函數
function* bar() { yield 'x'; yield* foo(); yield 'y'; } // 等同於 function* bar() { yield 'x'; yield 'a'; yield 'b'; yield 'y'; } // 等同於 function* bar() { yield 'x'; for (let v of foo()) { yield v; } yield 'y'; } for (let v of bar()){ console.log(v); } // "x" // "a" // "b" // "y"
let obj = { * myGeneratorMethod() { ··· } }; //等價於 let obj = { myGeneratorMethod: function* () { // ··· } };
function* main() { var result = yield request("http://some.url"); var resp = JSON.parse(result); console.log(resp.value); } function request(url) { makeAjaxCall(url, function(response){ it.next(response); }); } var it = main(); it.next();
更多Generator 函數的異步應用:http://es6.ruanyifeng.com/#do...
http://jsfiddle.net/Yoghurts/...
ES7引入 async await,使其更加好用
// 暫停 function sleep(numberMillis) { var now = new Date(); var exitTime = now.getTime() + numberMillis; while (true) { now = new Date(); if (now.getTime() > exitTime){ return; } } } // 消費者 function* consumer(name) { console.log(`${name}準備吃包子啦!`); while (true) { var baozi = yield; baozi += 1; console.log(`第${baozi}個包子來了,被分紅了兩份,一份被${name}吃了!`); } } // 生產者 function producer(name) { c1 = consumer('A'); c2 = consumer('B'); console.log(`${name}:我開始準備作包子了!`); c1.next(); c2.next(); for (let i = 0; i < 10; i++) { sleep(1000); c1.next(i); c2.next(i); } } // 小明開始生產包子,A和B同時開始吃包子 producer('小明')
小明:我開始準備作包子了! A準備吃包子啦! B準備吃包子啦! 第1個包子來了,被分紅了兩份,一份被A吃了! 第1個包子來了,被分紅了兩份,一份被B吃了! 第2個包子來了,被分紅了兩份,一份被A吃了! 第2個包子來了,被分紅了兩份,一份被B吃了! 第3個包子來了,被分紅了兩份,一份被A吃了! 第3個包子來了,被分紅了兩份,一份被B吃了! 第4個包子來了,被分紅了兩份,一份被A吃了! 第4個包子來了,被分紅了兩份,一份被B吃了! 第5個包子來了,被分紅了兩份,一份被A吃了! 第5個包子來了,被分紅了兩份,一份被B吃了! 第6個包子來了,被分紅了兩份,一份被A吃了! 第6個包子來了,被分紅了兩份,一份被B吃了! 第7個包子來了,被分紅了兩份,一份被A吃了! 第7個包子來了,被分紅了兩份,一份被B吃了! 第8個包子來了,被分紅了兩份,一份被A吃了! 第8個包子來了,被分紅了兩份,一份被B吃了! 第9個包子來了,被分紅了兩份,一份被A吃了! 第9個包子來了,被分紅了兩份,一份被B吃了! 第10個包子來了,被分紅了兩份,一份被A吃了! 第10個包子來了,被分紅了兩份,一份被B吃了!
//之前 function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function () { return '(' + this.x + ', ' + this.y + ')'; }; var p = new Point(1, 2);
ES6 的class能夠看做只是一個語法糖,它的絕大部分功能,ES5 均可以作到,新的class寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。
//es6 class Point { //構造方法 constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } var p = new Point(1, 2);
class Point { } class ColorPoint extends Point { }
在 ES6 以前,社區制定了一些模塊加載方案,最主要的有 CommonJS 和 AMD 兩種。前者用於服務器,後者用於瀏覽器。ES6 在語言標準的層面上,實現了模塊功能,並且實現得至關簡單,徹底能夠取代 CommonJS 和 AMD 規範,成爲瀏覽器和服務器通用的模塊解決方案。
ES6 的模塊自動採用嚴格模式,無論你有沒有在模塊頭部加上"use strict";
。
嚴格模式主要有如下限制:
定義模塊的對外接口。
一個模塊就是一個獨立的文件。該文件內部的全部變量,外部沒法獲取。若是你但願外部可以讀取模塊內部的某個變量,就必須使用export關鍵字輸出該變量。
如下是幾種用法:
//------輸出變量------ export var firstName = 'Michael'; export var lastName = 'Jackson'; //等價於 var firstName = 'Michael'; export {firstName}; //推薦,能清除知道輸出了哪些變量 //------輸出函數或類------ export function multiply(x, y) { return x * y; }; //------輸出並as重命名------ var v1 = 'Michael'; function v2() { ... } export { v1 as streamV1, v2 as streamV2 }; //------輸出default------ export default function () { ... }
注意:export default
在一個模塊中只能有一個。
使用export命令定義了模塊的對外接口之後,其餘 JS 文件就能夠經過import命令加載這個模塊。
如下是幾種用法,必須和上面的export對應:
//------加載變量、函數或類------ import {firstName, lastName} from './profile.js'; //------加載並as重命名------ import { lastName as surname } from './profile.js'; //------加載有default輸出的模塊------ import v1 from './profile.js'; //------執行所加載的模塊------ import 'lodash'; //------加載模塊全部輸出------ import * as surname from './profile.js';
若是在一個模塊之中,先輸入後輸出同一個模塊,import語句能夠與export語句寫在一塊兒。
export { foo, bar } from 'my_module'; // 等同於 import { foo, bar } from 'my_module'; export { foo, bar };
原始數據類型Symbol,表示獨一無二的值。
let s = Symbol(); typeof s // "symbol"
Symbol函數能夠接受一個字符串做爲參數,表示對 Symbol 實例的描述,主要是爲了在控制檯顯示,或者轉爲字符串時,比較容易區分。
let s1 = Symbol('foo'); let s2 = Symbol('bar'); s1 // Symbol(foo) s2 // Symbol(bar) s1.toString() // "Symbol(foo)" s2.toString() // "Symbol(bar)"
代理器(攔截器),Proxy 能夠理解成,在目標對象以前架設一層「攔截」,外界對該對象的訪問,都必須先經過這層攔截,所以提供了一種機制,能夠對外界的訪問進行過濾和改寫。
var proxy = new Proxy(target, handler);
var obj = new Proxy({}, { get: function (target, key, receiver) { console.log(`getting ${key}!`); return target.key; }, set: function (target, key, value, receiver) { console.log(`setting ${key}!`); return target.key=value; } });