ES6學習筆記
在學習ES6的過程中做的一些記錄,用于未來的快速回憶。
let&const
作用域的概念
- ES6新增塊級作用域的概念(一個大括號括起來的部分就是一個塊作用域)
- let與const用于在塊級作用域中聲明變量,該變量僅在當前塊級作用域有效
- ES6強制啟用嚴格模式,變量未聲明不允許使用
如何使用let與const
- let關鍵詞用于聲明變量
- const關鍵詞用于聲明常量,聲明時必須賦值
- let&const關鍵詞不能在同一個塊級作用域內重復聲明一個變量
- const定義的基本類型常量不能被修改,但是引用類型常量內部的值能被修改,只要不改變常量的引用即可
解構賦值
ES6允許按照一定模式,從數組和對象中提取值,對變量進行賦值。
數組解構賦值
// 完全解構
//本質上這種寫法屬于‘模式匹配‘,只要等號兩邊的模式相同,左邊的變量就會被賦予對應的值
let [a,b,c] = [1,2,3]; // a=1,b=2,c=3
let [a,[b]] = [1, [2]]; // a=1,b=[2]
let [,,a] = [1,2,3]; // a=3
let [d,...rest] = [1,2,3,4,5,6]; // d=1,rest=[2,3,4,5,6]
// 解構不成功
// 如果解構不成功,變量的值就等于undefined
let [x,y] = ['xy'] //x='xy',y=undefined
// 不完全解構
let [i,j] = [1,2,3]; // i=1,j=2
//如果等號的右邊不是數組,或者說不是可遍歷的結構,那么將會報錯
let [a] = 1; // 這里會報錯:Uncaught TypeError: 1 is not iterable
默認值
解構賦值允許有默認值。
let [x,y='b'] = ['a'];
console.log(y); //blet [x,y = 'b'] = ['a',undefined];
console.log(y); //b
// 數組成員為undefined時,默認值仍會生效
// 因為在ES6內部使用嚴格相等運算符‘===‘,判斷一個位置是否有值,
// 所以當一個數組成員嚴格等于undefined,默認值才會生效。let [x,y = 'b'] = ['a',null];
console.log(y); //null
// 如果一個數組成員是null,默認值就不會生效,因為null不嚴格等于undefined
對象解構賦值
對象的解構與數組有一個重要的不同,數組的元素是按次序排列的,變量的取值由它的位置決定;而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。
// 變量名與屬性名一致的情況下
let {foo,bar} = {foo : "aaa",bar : "bbb"}
console.log(foo); //aaa
console.log(bar); //bbb
// 實際上 對象的解構賦值是以這樣的形式簡寫的
let {foo : foo ,bar : bar} = {foo : "aaa",bar : "bbb"}
// 變量名與屬性名不一致的情況下,必須這樣寫
let {a : name, b : age} = {a : 'zhangsan', b : 33};
console.log(name); //zhangsan
console.log(age); //33
對象的解構賦值的內部機制,是先找到同名屬性,然后再賦值給對應的變量,真正被賦值的是后者,而不是前者,第一個foo/bar 是匹配的模式,對應的foo/bar屬性值才是變量,真正被賦值的是屬性值(也就是第二個foo/bar)。
字符串解構賦值
const [a,b,c,d,e] = 'hello';
console.log(a); //h
console.log(b); //e
console.log(c); //l
console.log(d); //l
console.log(e); //olet { length : len} = 'yahooa';
console.log(len);
//類似數組的對象都有一個length屬性,還可以對這個屬性解構賦值
布爾值/數值解構賦值
解構賦值時,如果等號右邊是數值和布爾值,則會先轉為對象,但是等號右邊為undefined 和 null時無法轉為對象,所以對他們進行解構賦值時,都會報錯。
let {prop : x } = undefined;
console.log(x);
//報錯:Uncaught TypeError: Cannot destructure property `prop` of 'undefined' or 'null'
函數參數解構賦值
function move({x = 0,y = 0} = { }){return [x,y];}
console.log(move({x : 3,y : 4})); //[3,4]
console.log(move({x : 3})); //[3,0]
console.log(move({})); //[0,0]
console.log(move()); //[0,0]
// move()的參數是一個對象,通過對這個對象進行解構,得到變量x、y的值,如果解構失敗,x和y 等于默認值
function move2({x,y} = {x : 1, y : 2 }){return [x,y];
}
console.log(move2({x : 6,y : 8})); //[6,8]
console.log(move2({})); //[undefined,undefined]
console.log(move2()); //[1,2]
// move2() 是為函數move的參數指定默認值,而不是為變量x和y指定默認值,
// 所以與前一種寫法的結果不太一樣,undefined 就會觸發函數的默認值
解構作用
-
改變變量的值
let x = 1; let y = 2; [x,y] = [y,x]; // x=2, y=1
-
從方法返回多個值
function example(){return {foo : 'a',bar : 'b'} } let {foo,bar} = example(); // foo='a',bar='b' function example222(){return [1,2,3,4,5]; } let [a,,...b] = example222(); // a=1, b=[3,4,5]
-
函數參數的定義
//參數是一組有次序的值 function example([x,y,z]){return x + y + z; } example([1,2,3]) console.log(example([1,2,3])); //6 //參數是一組無次序的值 function f({x,y,z}){return x + y + z; } f({x : 'a', z : 'b', y : 'c' }); console.log(f({x : 'a', z : 'b', y : 'c' })); //acb
- 提取JSON數據
- 函數參數的默認值
- 輸入模塊的指定用法
正則擴展
構造函數的變化
// ES5 的方式
let reg = new RegExp('xyz', 'i');
let reg2 = new RegExp(/xyz/i);
reg.test('xyz123'); // true
reg.test('xyz123'); // true
// ES6 方式的變化,可以有第二個參數用作修飾符
// 這種方式,第二個參數的修飾符會覆蓋第一個正則表達式的修飾符
let reg3 = new RegExp(/xyz/ig, 'i')
reg3.flags // i
u修飾符
// u -> unicode,用于處理unicode編碼的正則匹配
// unicode中4個字節 = 一個字母
/^\uD83D/.test('\uD83D\uDC2A'); // true \uD83D\uDC2A 被當做兩個字母來處理
/^\uD83D/u.test('\uD83D\uDC2A'); // false,\uD83D\uDC2A 被當作一個字母來處理
// 正則中,大括號里面若是unicode編碼,需添加u修飾符才能被識別
/\u{61}/.test('a'); // false
/\u{61}/u.test('a'); // true
// 用'.'操作符識別大于兩個字節的字符需要加上u修飾符
// '.'操作符不能處理換行符/回車符/行分隔符/段分隔符
/^.$/.test('吉'); // false
/^.$/u.test('吉'); // true
/吉{2}/.test('吉吉'); // false
/吉{2}/u.test('吉吉'); // true
// 凡是超過兩個字節的匹配都需要添加 u 修飾符
y修飾符
let s = 'bbb_bb_b';
let a1 = /b+/g;
let a2 = /b+/y;
a1.exec(s); // g修飾符全局匹配,不強調從下一個字符開始匹配,只要接下來的字符能匹配成功就ok
a2.exec(s); // y修飾符全局匹配,必須緊跟著下一個字符開始匹配
// sticky屬性用于判斷是否開啟了y修飾符
a1.sticky; // false
a2.sticky; // true
s修飾符
未實現,僅作為提案
// '.'操作符不能處理換行符/回車符/行分隔符/段分隔符
// 但添加 s 修飾符就可以
字符串擴展
Unicode表示法
console.log(`\u0061`); // a
console.log(`\u20887`); // []7,會將20087拆成2008 / 7
console.log(`\u{20887}`); // ?, 大括號括起來后,將作為一個字符處理// 每兩個字節為一個單位長度
// ES5 不能很好的處理長度超過兩個字節的字符
let s = '?';
s.length; // 2
'a'.length; // 1
s.codePointAt(0); // 可以很好的處理長度超過兩個字節的字符的碼值,將字符轉為碼值
String.fromCodePoint("0x20087"); // 將碼值轉為字符
遍歷接口
let str "\u{20bb7}abc";
// ES5的遍歷方式,不能很好的處理長度超過兩個字節的字符
for(let i=0; i<str.length; i++) {console.log(str[i]);
}
// 新的遍歷接口,可以自動處理任何字符
for (let code of str) {console.log(code);
}
模板字符串
let name = 'list';
let info = 'hello world';
let meg = `This is ${name}, ${info}`;
console.log(meg);
數值擴展
新增方法
Number.isFinite(); // 判斷是否為有窮數
Number.isNaN(); // 判斷是否不是數字
Number.isInteger(); // 判斷是否為整數 , 25/25.0均為true,25.1為false,參數必須為數值
Number.MAX_SAFE_INTEGER // 數的上限,ES5
Number.MAX_SAFE_INTEGER // 數的下限,ES5
Number.isSafeInteger(); // 判斷一個數是否在有效范圍內
Math.trunc(); // 取整數部分
Math.sign(); // 正數返回1,負數返回-1,0返回0
Math.cbrt(); // 求立方根
// 還補充了三角函數方法、對數方法
方法調整
將ES5中某些全局方法移到對象下面,如parseInt()
由全局移動到Number
對象。
數組擴展(新增特性)
Array.from
// 從類數組中創建數組
// 如頁面中有一堆 p 標簽
let p = document.querySelectorAll('p');
let pArr = Array.from(p);
// 此時,將集合p轉為了數組pArr// 將數組中的元素按照function的規則處理后并返回一個數組
Array.from([1,3,5], function(item){return item*2}); // [1,6,10]
Array.of
let arr = Array.of(1,2,3,4,5); // arr=[1,2,3,4,5]
let emptyArr = Array.of(); // emptyArr=[]\
copyWithin
let arr = [1,2,3,4,5].copyWithin(0, 3, 5);
// arr = [4, 5, 3, 4, 5]
// 將起始位置3到終止位置5的元素覆蓋掉從0開始的元素(不包括下標為5的元素)
let arr2 = [1,2,3,4,5].copyWithin(0, 1, 5);
// arr2 = [2, 3, 4, 5, 5];
find
/findIndex
let val = [1,2,3,4,5,6].find(function(item){return item>3});
// val = 4
// find找到一個符合條件的元素就結束,并返回該元素的值
let index = [1,2,3,4,5,6].findIndex(function(item){return item>3});
// index = 3
// findIndex找到一個符合條件的元素就結束,并返回該元素的下標
fill
let arr = [1,'a',undefined];
arr.fill(6); // arr=[6,6,6]
// 將起始位置1到終止位置3的元素替換成5(不包括下標為3的位置)
arr.fill(5,1,3); // arr=[6,5,5]
entries
/keys
/values
for (let index of [1,2,3].keys()){console.log(index); // 0,1,2
}
for (let value of [1,2,3].values()){console.log(value); // 1,2,3
}
for (let [index,value] of [1,2,3].entries()){console.log(index, value); // 0,1;1,2; 2,3
}
includes
let a = [1,2,NaN].includes(1); // a = true
let b = [1,2,NaN].includes(NaN); // b = true
函數擴展
參數默認值
// 有默認值的參數右邊的參數都需要有默認值
function test(x, y='hello world') {console.log("默認值:", x, y);
}
test('hhh'); // 觸發默認值
test('hhh', 'aaa'); // 不觸發默認值
// 默認參數的作用域
let x = "test";
function test(x, y=x) {console.log(x, y);
}
function test2(c, y=x) {console.log(x, y);
}
test("hhh"); // hhh hhh
test2("hhh"); // hhh test
// 采用這種方式傳遞默認值時,要注意等號右邊變量的作用域
// 等號右邊的變量的值與最近定義的同名變量的值相同
rest參數
// rest參數后不能有其他參數
function test3(...arg) {for(let v of arg) {console.log('rest:', v)}
}
擴展運算符
// 擴展運算符會將解構數據
console.log(...[1,2,3]); // 1;2;3
console.log("a", ...[1,2,3]); // a;1;2;3
箭頭函數
// 單個參數時可以不使用括號,多個參數時需要使用括號將其括起來
// 函數體有多個語句時,使用大括號括起來
let arrow = v => v*2;
arrow(3); // 6let arrow2 = () => 5;
arrow2(); // 5
尾調用
// 函數內部的最后一句是另一個函數
// 尾調用可以提升JS的性能
// 下面例子中,fx()就實現了尾調用
function tail(x) {console.log(x);
}
function fx(x) {return tail(x);
}
fx(123); // 123
對象擴展(新增特性)
簡潔表示法
// 當對象中的屬性名與變量名相同時,可以只寫一個
// 對象里面有方法,在ES6中可以省略function關鍵字
let o = 1;
let k = 2;
let obj = {o,k,hello () {console.log('hhh');}
}
屬性表達式
// ES5中,對象中的key是固定的
// ES6中,key是可以用表達式或者變量的
let a = 'b';
let es5_obj = { // {a:c}a: 'c'
}
let es6_obj = { // {b:c}[a]: 'c'
}
擴展運算符(ES7提案)
// 不建議使用,支持不好
let {a,b,...c} = {a:'1', b:'2', c:'3', d:'5'};
// a = 1
// b = 2
// c = {c:'3', d: '5'}
Object新增方法
Object.is('abc', 'abc'); // 與===的功能相同
Object.assign({a:'1'}, {b:'2'}); // 將第二個對象的內容追加到第一個對象里,淺拷貝
// 只拷貝自身的數據,不拷貝繼承的屬性以及不可枚舉的屬性// 遍歷對象
let obj = {a:'1', b:'2'};
for(let [key, value] of Object.entries(test)) {console.log([key, value]);
}
Symbol
Symbol概念
這種數據類型提供一個獨一無二的值。
Symbol的作用
-
聲明
// 方法1 let a1 = Symbol(); let a2 = Symbol(); console.log(a1 === a2); // false // 方法2 // 在生成a3前,會檢查‘a3’在全局是否存在 // 若存在,則為取值 // 若不存在,則為生成一個新的Symbol let a3 = Symbol.for('a3')
-
作用
// 可用于處理對象里面的重名,防止沖突 let a1 = Symbol.for('abc');
[a1]: '123','abc': '5','c': '6'
}
// 常規循環只能處理非Symbol值
for(let [key, value] of Object.entries(obj)) {
console.log(key, value); // {abc:'5', c:'6'}
}
// 這種方式只能處理Symbol值
Object.getOwnPropertySymbol(obj).foreach(function(item) {
console.log(obj[item]); // 123
})
// 這種方式能夠處理以上兩種情況
Reflect.ownKeys(obj).foreach(function(item) {
console.log(obj[item]); // 123,5,6
})`
Map & Set 數據結構
Set
- Set中的元素是不能重復的
- Set可用于數組去重
- Set里面不會自動數據類型轉換
let list = new Set();
list.add(5); // 5
list.add(6); // 5,6
list.size; // 2let arr = [1,2,3,4,5];
let list2 = new Set(arr);
list2.size; // 5let list3 = new Set([2, '2']); // 2, '2'// 幾個Set常用方法
let set = new Set([1,2,3]);
set.add(5); // 添加元素
set.has(2); // 判斷是否有某個元素
set.delete(3); // 刪除元素
set.clear(); // 清空Set// 遍歷Set
let set2 = new Set([2, '2']);
// 使用keys()與values()的效果一樣,因為Set中鍵值一致
// 不使用keys()、values(),直接遍歷Set也可以
// 也可以使用entries()
for(let key of set2.keys()) {console.log(key); // 2, '2'
}
for(let value of set2.values()) {console.log(value); // 2, '2'
}
for(let val of set2) {console.log(val); // 2, '2'
}
for(let [key,value] of set2.values()) {console.log([key, value]); // 2, '2'
}
WeakSet
- WeakSet的元素只能是對象
- WeakSet存儲的是弱引用,且不會檢測是否被垃圾回收機制回收
let weakList = new WeakSet();
let arg = {};
weakList.add(arg); // arg
// WeakSet沒有clear方法、沒有size屬性、不能遍歷
Map
- 任意數據類型都可以作為Map的數據類型
let map = new Map();
let arr = ['123'];
map.set(arr, 123); // ['123']=>123
map.get(arr); // 123// Map可以接收一個二維數組作為參數
let map2 = new Map([['a',123],['b',456]]);
console.log(map2); // 'a'=>123, 'b'=>456
map2.size; // 2
map2.delete('a'); // 'b'=>456
map2.clear(); // 清空Map
WeakMap
- WeakMap的key只能是對象
- WeakMap沒有clear方法、沒有size屬性、不能遍歷
- WeakMap存儲的是弱引用,且不會檢測是否被垃圾回收機制回收
let weakMap = new WeakMap();
let arg = {};
weakMap.set(arg, 123);
weakMap.get(arg); // 123
Map和Array的對比
let map = new Map();
let array = [];
// add
map.set('t', 1); // 't'=>1
array.push({t:1}); // {t:1}
// query
let map_exist = map.has('t'); // true
let array_exist = array.find(item=>item.t); // {t:1}
// modify
map.set('t', 2); // 't' => 2
array.forEach(item => item.t?item.t=2:''); // {t:2}
// delete
map.delete('t');
let index = array.findIndex(item => item.t);
array.splice(index, 1);
Set和Array的對比
let set = new Set();
let arr = [];
// add
set.add({t:1});
arr.push({t:1});
// query
let set_exist = set.has({t:1}); // false,因為是不同的引用
let arr_exist = array.find(item=>item.t); // {t:1}
// modify
set.forEach(item => item.t?item.t=2:''); // 若直接add,則不能修改成功,因為引用不同
arr.forEach(item => item.t?item.t=2:''); // 這里遍歷是確保{t:1}存在
// delete
set.forEach(item => item.t?delete(item):'');
let index = array.findIndex(item => item.t);
array.splice(index, 1);
Map、Set、Object的對比
let item = {t:1};
let map = new Map();
let set = new Set();
let obj = {};
// add
map.set('t', 1);
set.add(item);
obj['t'] = 1;
// query
let map_exist = map.has('t');
let set_exist = set.has(item);
let obj_exist = 't' in obj;
// modify
map.set('t', 2);
item.t = 2; // 這里,set存儲的是引用,因此對對象的修改也會修改set里面對應的值
obj['t'] = 2;
// delete
map.delete('t');
set.selete(item);
delete obj['t'];
Proxy與Reflect
Proxy的使用
// Proxy為代理,在用戶和對象之間設置代理,防止用戶直接操作對象
// 同時,可以在代理上做一些操作來修改查詢到的內容
// 或者限制用戶對某些屬性的修改行為
let obj = {time: '2018-11-21',name: 'net',_r: 123
};
let monitor = new Proxy(obj, {// 攔截對象屬性的讀取// 這里,當查詢的value中有2018字符串時將其替換成2019get (target, key) {return target[key].replace('2018', '2019'); // 將2018替換成2019},// 攔截對象屬性的修改// 這里只允許對name的修改set (target, key, value) {if(key === 'name') {return target[key] = value;} else {return target[key];}},// 攔截key in obj操作,限制暴露哪些屬性// 此處只允許判斷是否存在name這個key,對于其它key一律返回falsehas (target, key) {if (key === 'name') {return target[key];} else {return false;}},// 攔截delete// 這里只允許刪除以下劃線開頭的屬性deleteProperty (target, key) {if (key.indexOf('_') > -1) {delete target[key];return true;} else {return target[key];}},// 攔截Object.keys,Object.getOwnPropertySymbols,Object.getOwnPropertyNames// 這里將time屬性屬性過濾掉ownKeys (target) {return Object.keys(target).filter(item => item!='time')}
});
monitor.time; // 2019-11-21
monitor.time = '2015-11-21'; // 2018-11-21
monitor.name = 'Chung'; // Chung
'name' in monitor; // true
'time' in monitor; // false
Reflect的使用
// 使用Reflect有以下好處
// 1.更加有用的返回值
// 2.函數操作,如判斷一個屬性是否在該obj里面, Reflect.has(obj, key)
// 3.更加可靠的函數式執行方式
// 4.可變參數形式的構造函數
// 5.控制訪問器或者讀取器的this
// Reflect用于代替直接操作對象
// 建議使用Reflect來操作對象
let obj = {time: '2018-11-21',name: 'net',_r: 123
};
Reflect.get(obj, 'time'); // 2018-11-21
Reflect.set(obj, 'name', 'Chung'); // name: 'Chung'
Reflect.has(obj, 'name'); // true
使用場景
// 使用Proxy和Reflect實現解耦的數據校驗
function validator (target, validator) {return new Proxy(target, {_validator: validator,set (target, key, value, proxy) {if (target.hasOwnProperty(key)) {let val = this._validator[key];if(!!val(value)) {return Reflect.set(target, key, value, proxy);} else {throw Error(`Can not set ${key} to ${value}!`);}} else {throw Error(`${key} not exist!`)}}})
}const personValidator = {name (val) {return typeof val === 'string';},age (val) {return typeof val === 'number' && val > 18;}
}class Person {constructor (name, age) {this.name = name;this.age = age;return validator(this, personValidator)}
}const person = new Person('hh', 18);
console.info(person); // {name: 'hh', age: '18'}
person.name = 20; // 不能設置為20,因為 personValidator 限制name的值只能是string
類和對象
基本定義和生成實例
// 定義類
class Person {constructor (name='Jarry') {this.name = name;}
}
// 生成實例
let person = new Person('Chung');
console.log(person); // Person {name: ‘Chung’}
繼承
// 定義類
class Person {constructor (name='Jarry') {this.name = name;}
}
// 使用extends繼承
class Student extends Person {constructor (name='Sunny') {super(name); // 將子類的參數傳遞給父類// 若子類有自己的屬性,則需要將自有屬性放在super()下面}
}
// 生成實例
let stu = new Student();
console.log(stu); // Student {name: 'Sunny'}
Getter 和 Setter
// 定義類
class Person {constructor (name='Jarry') {this.name = name;}// longName是屬性而不是方法get longName () {return 'Good ' + this.name;}set longName (value) {this.name = value;}
}
// 生成實例
let person = new Person();
person.longName; // 'Good Jarry'
person.longName = 'Chung'; // 'Good Chung'
靜態屬性和靜態方法
// 定義類
class Person {constructor (name='Jarry') {this.name = name;}// 使用static關鍵字定義靜態方法static tell () {console.log('hello');}
}
// 定義完類后再定義靜態屬性
Person.age = '18';// 調用靜態方法,使用類名調用而非實例調用
Person.tell(); // 'hello'
Person.age; // 18
Promise
- Promise是異步編程的一種解決方案
Promise基本使用
// 以回調函數處理異步
let ajax = function(callback) {console.log('執行1');setTimeout(function() {callback && callback.call();}, 1000)
}
ajax(function() {console.log('Timeout One');
})// 以Promise處理異步
let ajax = function() {console.log("執行2");return new Promise(function(resolve, reject) {setTimeout(function() {resolve();}, 1000)})
}
ajax().then(function() {console.log('Timeout Two');
})
// 上面的結果為:
// 執行1
// 執行2
// Timeout One
// Timeout Two
Promise使用場景
// 所有圖片加載完再添加到頁面
function loadImg(src) {return new Promise((resolve, reject) => {let img = document.createElement('img');img.src = src;img.onload = function () {resolve(img)}img.onerror = function () {reject(err)}})
}function showImgs(imgs) {imgs.forEach(function(img) {document.body.append(img)})
}
// all 將多個promise當作一個,全部加載完后才執行resolve
Promise.all([loadImg('http://i4.buimg.com/567571/df1ef0720bea6832.png'),loadImg('http://i4.buimg.com/567571/df1ef0720bea6833.png')
]).then(showImgs)
// race, 數組中某一個執行完就馬上執行resolve
Promise.race([loadImg('http://i4.buimg.com/567571/df1ef0720bea6832.png'),loadImg('http://i4.buimg.com/567571/df1ef0720bea6833.png')
]).then(showImgs)
Iterator
-
for...of
本質上是使用Iterator接口
let arr = [1,2];
let map = arr[Symbol.iterator]();
map.next(); // {value:1, done:false}
map.next(); // {value:2, done:false}
map.next(); // {value:undefined, done:true}// 為對象實現iterator接口并指定遍歷方式
let obj = {start: [1,2,3],end: [7,8,9],[Symbol.iterator] () {let seft = this;let index = 0;let arr = seft.start.concat(self.end);let len = arr.length;return {next () {if (index < len) {return {value: arr[index++],done: false}} else {return {value: arr[index++],done: true}}}}}
}for(let key of obj) {console.log(key); // 1,2,3,7,8,9
}
Generator
Generator基本定義與使用
let tell = function* () {yield 'a';yield 'b';return 'c';
}
let k = tell();
k.next(); // {value:'a', done:false}
k.next(); // {value:'b', done:false}
k.next(); // {value:'c', done:true}
k.next(); // {value:'undefined', done:true}let obj = {};
obj[Symbol.iterator] = function* () {yield 1;yield 2;yield 3;
}
for(let value of obj) {console.log(value); // 1,2,3
}
實例
// 應用1:狀態機
// 假設某一事物只有三種狀態,該事物僅在這三種狀態中轉換
// 以下生成器使得state僅在A、B、C間轉換
let state = function* () {while(1) {yield 'A';yield 'B';yield 'C';}
}// 應用2:限制抽獎次數
let draw = function(count) {// 此處為具體的抽獎邏輯console.log(`Remain ${count}`);
}let residue = function* (count) {while (count>0) {count --;yield draw(count);}
}// 應用3:長輪詢
let ajax = function* () {yield new Promise(function(resolve, reject) {setTimeout(() => {resolve({code:0});}, 200);})
}let pull = function() {let generator = ajax();let step = generator.next();step.value.then(function(val) {if(val.code !== 0) {setTimeout(() => {console.log('wait');pull();}, 1000);} else {console.log(val);}})
}pull();
Decorator
- 修飾器是一個函數,用來修改類的類型
- 第三方庫core-decorators提供了現成的修飾器
-
解決了兩個問題
- 不同類間共享方法
- 編譯期對類和方法的行為進行改變
-
參數
- target - 目標類
- name - 屬性
- descriptor - 屬性描述符
// *修飾器特性在將來的ES版本可能會被移除
let readonly = function (target, name, descriptor) {descriptor.writable = false;return descriptor;
}class Test{@readonlytime () {return '2018-11-21';}
}
let t = new Test();
t.time = function(){}; // 會報錯,因為修飾器不允許對該方法進行修改// 也可修飾整個類
let typename = function (target, name, descriptor) {target.myname = 'hello';
}
@typename
class Name{}
console.log(Name.myname);// 實例:日志系統的埋點
let log = (type) => {return function (target, name, descriptor) {let src_method = descriptor.value;descriptor.value = (...arg) => {src_method.apply(target, arg);console.log(`log ${type}`);}}
}class AD{@log('show')show () {console.info('ad is shown');}@@log('click')click () {console.log('ad is clicked')}
}