ES6新特性簡介
環境安裝
npm install -g babel
npm install -g babel-node //提供基於node的REPL環境
//創建 .babelrc 文件
{"presets": ["es2015"]}
npm install babel-presets-es2015
箭頭函數和詞法this
箭頭函數的用法類似phython
, coffeeScript
和 java8
//expression func body, (僅有1參數 括號可省略)
var odds = evens.map(v => v+1);
var nums = evens.map((v,i) => v + i);
//statement func body
nums.forEach( v => {
if(v % 5 === 0){
fives.push(v);
}
});
//lexical this
var sindy = {
name: 'Sindy',
friends: ['nami', 'robin'],
printFriends() {
//sindy.printFriends() this指向sindy,
//forEach迭代函數內部this,跟它定義的上下文this一致
this.friends.forEach(f=>{
console.log(this.name + ' knows ' + f);
});
}
};
類和繼承
class
和 extend
是一種語法糖,也是基於原型繼承實現的。 class
支持 super calls
, 實例化,靜態方法和構造函數。
class關鍵字 聲明類
//class聲明類 內部直接是屬性和方法 不用,分隔。 constructor
class Person{
constructor(name, age){
this.name = name;//實例屬性
this.age = age;
}
sayhi(name){//原型對象的方法
console.log(this.name+' say hello to ' + name);
}
static like(){//靜態方法
console.log('i want to be freedom king');
}
}
extends關鍵字 聲明繼承
//繼承關鍵字 extends
class Zoro extends Person{
constructor(fav){
super('zoro', 18); //constructor中, super引用父類的構造函數
this.fav = 'keep going';
// console.log(super); //error 不能直接打印super
console.log(super('cici',9),'<--c');
}
sayhi(){
// console.log(super); //error 不能直接打印super
console.log(super.sayhi(),'<--m'); //原型對象的方法中, super引用父類的原型對象
super.sayhi();//調用父類原型對象的方法
}
}
增強的對象字面量
書寫對象字面量更方便簡潔。 如:foo:foo
的簡寫, 函數定義簡寫, 動態屬性名。
var obj = {
__proto__: theProtoObj, //設置原型對象
['__proto__']: somethingElse, //['__proto__']表示計算的屬性名,不會認為是設置原型對象, 有時也可避免提早報同名屬性的錯誤
handler, // hanlder: handler 的簡寫
toString(){//類似定義類 命名函數表達式的名稱作為key
//super calls
return 'd' + super.toString(); //super 應該指向 theProtoObj
},
['prop_'+(()=>23)]: 23 //[expression] 中括號表示內部為屬性名的計算表達式,可實現動態屬性名
}
__proto__
屬性在node下都支持,但在瀏覽器環境不一定都支持, chrome是支持的。
模板字符串
模板字符串跟php雙引號字符串類似 "hello ,$name"
,字符串中可以解析變量和語句, 對於構造字符串是很方便的。
//單行
`this is an alone night`
//多行
`In ES5 this is
not legal`
//變量插值
var name = 'idle', time='today';
var greeting = `hello ${name}, how are you ${time}`;
//不轉義
String.raw`in es5 "\n" is a line-feed`
解構
參數解構 對象解構 數組解構, 解構是一種十分方便的語法特性. 當結構不對應解析失敗時,會對應的返回undefined,不報錯.
//解構賦值
var [a, b] = [1,2]; // a===1, b===2
var {name, age} = getPerson();
//參數解構
function hello({name: x}){
console.log('hello, ', x);
}
hello({name: 'alice'});
//解構失敗
var [a] = []; // a===undefined
//提供解構失敗時的默認值
var [a=1] = []; //a===1
//參數解構失敗的默認值
function r({x,y,w=10,h=20}){
return x+y+w+h;
}
r({x:1, y:2});
//參數默認值
function f(a, b=10){
return a+b;
}
f(5);
//rest arguments
function f(x,...y){//剩余形參
// y is an array
return x * y.length
}
f(3, 'hello', true);//6
function f(x,y,z){
return x+y+z;
}
f(...[2,3,4]); //=>9 剩余實參
塊級作用域
let
和 const
聲明的變量和常量都是屬於塊級作用域的.
function f(){
let x;
{
const x = 'sindy'; //ok 因為塊級作用域
x = 'foo'; //error 因為x是常量
}
x = 'bar'; //在let 之后賦值,沒問題
let x = 'inner'; //error 重復聲明
}
迭代對象(iterator) 和 for .. of ..
迭代對象允許自定義迭代方式,如:
let fibonacci = {
[Symbol.iterator]() {
let pre=0, cur=1;
return {//返回包含next方法的迭代對象(iterator)
next(){
[pre, cur] = [cur, pre+cur];
return {done: false, value: cur}; //iteratorResult
}
}
}
};
for(var n of fibonacci){
if(n>100){//若無終止條件會一直迭代下去 n應該代表的是每次迭代的結果
break;
}
console.log(n);
}
//typescript風格的iterator接口定義
interface IteratorResult{
done: boolean,
value: any
}
interface Iterator {
next(): IteratorResult //包含next方法,且next()返回IteratorResult類型結果
}
interface Iterable {
[Symbol.iterator](): Iterator // fibonacci() 執行后返回Iterator
}
生成器generators
function*
和 yield
, function*
聲明的函數返回一個generator實例. generator是iterator的子類型,它包含自己的throw
和next
generator可以實現await風格的異步編程
//generator版的fibonacci
var fibonacci = {
[Symbol.iterator]: function*() {//function*聲明
var pre=0, cur=1;
for(;;){//不會終止的循環?
var temp = pre;
pre = cur;
cur += pre;
yield cur;//generator實例每次執行都從yield的地方重新開始?
}
}
};
for(var n of fibonacci){
if(n > 100){
break;
}
console.log(n);
}
//typescript風格的generator接口定義
interface generator extends iterator {
next(value?: any): IteratorResult; //next方法可帶參數, yield返回參數給next?
throw(exception: any);
}
注意 iterator and generator 目前需要polyfill才能使用, 新版的chrome已支持.
unicode的完全支持
// same as ES5.1
"𠮷".length == 2
// new RegExp behaviour, opt-in ‘u’
"𠮷".match(/./u)[0].length == 2
// new form
"\u{20BB7}" == "𠮷" == "\uD842\uDFB7"
// new String ops
"𠮷".codePointAt(0) == 0x20BB7
// for-of iterates code points
for(var c of "𠮷") {
console.log(c);
}
模塊(modules)
ES6有不同於amd
和 cmd
的模塊風格
//>lib/math.js
export function sum(x,y){
return x + y;
}
export var fnum = 3.122323;
//>app.js
import * as math from 'lib/math';
console.log(math.sum, math.fnum);
//>other.js
import {fnum, sum} from 'lib/math'; //對象解構賦值
console.log(fnum, sum);
一些額外的特性 export default
和 export *
//>lib/otherMath.js
export * from 'lib/math'; //導出math.js的接口
export var e = 2.323;
export default function(x){
return Math.exp(x);
}
//>app.js
import exp, {sum , e} from 'lib/otherMath';
模塊導出 export
var name = 'sindy';
var age = 20;
//相當於node module.exports = {name:name, age:age};
export {name, age};
//相當於module.exports.default = function getName(){..}
export default function getName(){
return name;
}
模塊導入 import
//import m from './modA'; m === modA.export.default
//import {name, age} from './modA' modA.exports 析構
import getName, {name, age} from './module-e.es';
console.log(name, age);
console.log(getName());
Map , Set, WeakMap, WeakSet
//Sets
var s = new Set();
s.add('hello').add('goodbye').add('hello');//已有則不再加入
s.size === 2; //true
//Maps
var m = new Map();
m.set('hello', 'good');
m.set(s, 23);
m.get(s) === 23;
//WeakMap
var wm = new WeakMap();
wm.set(s, {extra:24});
vm.size === undefined;
//WeakSet
var ws = new WeakSet();
ws.add({data:12});
proxies
攔截或代理對象的屬性讀寫,函數的調用。
//proxy a normal object
var target = {};//被代理的對象
var handler = {//代理哪些方法
get: function(reciever, name){//get代理對屬性的訪問
return `hello, ${name}`;
}
};
var p = new Proxy(target, handler);
p.world === 'hello, world';
//proxy a function object
var target = function(){ console.log('i am the target'); };
var handler = {
apply: function(reciever,...args){//代理 target()
console.log('i am the proxy');
}
};
//代理的類型
var handler = {
//target.prop
get: ...,
//target.prop = val
set: ...,
//prop in target
has: ...,
//delete target.prop
deleteProperty: ...,
//target(args)
apply: ...,
//new target(args)
constructor: ...,
//Object.getOwnPropertyDescriptor(target, prop)
getOwnPropertyDescriptor: ...,
//Object.defineProperty(target, prop, descriptor)
defineProperty: ...,
//Object.getPrototypeOf(target), target.__proto__
//obj.isPrototypeOf(target), obj2 instanceof target
getPrototypeOf: ...,
//Object.setPrototypeOf(target, proto)
setPrototypeOf: ...,
//for(var i in target)
enumerate: ...,
//Object.keys(target)
ownKeys: ...,
//Object.preventExtensions(target)
preventExtensions: ...,
//Object.isExtensible(target)
isExtensible: ...
}
標識符 symbol
(function(){
//module scope symbol
var key = Symbol('key');
function MyClass(privateData){
this[key] = privateData;
}
MyClass.prototype = {
dostuff: function(){
...this[key]..
}
};
typeof key === 'symbol';
})();
var c = new MyClass();
c['key'] === undefined;
內置的子類
Array
, Date
,Element
等成為內置的子類。
class MyArray extends Array{
constructor(...args){
super(...args);
}
}
var arr = new MyArray();
arr[1] = 12;
arr.length === 2;
Math + Number + String + Object API擴展
Number.isInterger(Ifinity); //false
Number.isNaN('NaN'); //false
Math.hypot(3,4); //5
'abcde'.includes('abc');
'abc'.repeat(2);
Array.from(document.querySelectAll('h2')); //return a real array
Array.of(1,2,3); => [1,2,3] 枚舉數組元素的形式 創建數據, 同 new Array(1,2,3); 但沒有僅有1各參數時的歧義
[0,0,0].fill(7,1); //從index==1開始填充7
[1,2,3].findIndex(x=>x==2); //返回使迭代器為真的元素的索引
['a','b','c'].entries(); //iterator [0,'a'], [1,'b'], [2, 'c']
['a','b','c'].keys(); //iterator 0,1,2
['a','b','c'].values(); //iterator 'a','b','c'
var alice = {name: 'Alice'};
Object.assign(alice, {age: 18}); //類似extend
二進制和八進制字面量
0b111110111 === 503
0o767 === 503
Promises
Promise
是一個異步編程的庫,存在於很多其他的JS庫中
function timeout(duration = 0){
return new Promise((resolve, reject) => {//返回稍后將被resolve的promise對象
setTimeout(resolve, duration);//定時resolve這個新new的promise
});
}
var p = timeout(1000).then(()=>{//promise鏈
return timeout(2000);
}).then(()=>{
throw new Error('test');
}).catch(err=>{
return Promise.all([timeout(100), timeout(200)]);
});
反射API(Reflect API)
var o = {a: 1};
Object.defineProperty(o, 'b', {value:2});
o[Symbol('c')] = 3;
Reflect.ownKeys(o); //['a','b',Symbol(c)]
function C(a,b){
return this.c = a + b;
}
var instance = Reflect.construct(C, [10,12]);
instance.c === 22;
尾調用 tail calls
防止stack overflow
function factorial(n, acc=1){//階乘計算
'use strict';
if(n <= 1){ return acc; }
return factorail(n-1, n*acc);
}
factorail(10000); //尾調用優化后不會 stack overflow