时间 | 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; } });