ES6學習筆記

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')}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450127.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450127.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450127.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

用jenkins創建節點

原料&#xff1a;(1)jre下載鏈接&#xff1a;https://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html (2)jdk:下載鏈接&#xff1a;https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 一、創建SLAVE節點…

統計git倉庫一些commit數據

基于git統計某個人的代碼提交行數 git log --author"xxx" --prettytformat: --since1.hour.ago --numstat | awk { add $1 ; subs $2 ; loc $1 - $2 } END { printf "added lines: %s removed lines : %s total lines: %s\n",add,subs,loc } - 統計倉…

JAXB: XML綁定的Java體系結構

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 用于XML綁定的Java體系結構&#xff08;JAXB&#xff09;是一種軟件框架&#xff0c;它允許Java開發人員將Java 類映射到XML表示。JAXB提…

解決 Script Error 的另類思路

2019獨角獸企業重金招聘Python工程師標準>>> 本文由小芭樂發表 前端的同學如果用 window.onerror 事件做過監控&#xff0c;應該知道&#xff0c;跨域的腳本會給出 "Script Error." 提示&#xff0c;拿不到具體的錯誤信息和堆棧信息。 這里讀者可以跟我一…

大平臺的局限

這篇文章算是二稿。初稿使的是慣用的賣弄筆法&#xff0c;寫到盡興時去查了查資料&#xff0c;哦草&#xff0c;錯了好多。悶悶不樂。后來就不敢再鬼扯&#xff0c;老老實實干巴巴地講觀點。 做產品的人都喜歡大平臺&#xff0c;好像男人都喜歡大胸脯女郎&#xff0c;但是胸脯大…

Lisenter筆記

EventListener與EventObject要完成在線用戶列表的監聽器&#xff0c;需要使用如下幾個接口&#xff1a;ServletContextListener接口&#xff1a;在上下文初始化時設置一個空的集合到application之中&#xff1b;HttpSessionAttributeListener接口&#xff1a;用戶增加session屬…

Android應用開發—重載fragment構造函數導致的lint errors

背景&#xff1a;在一次release打包中發現lint報以下錯誤&#xff1a; Error: Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead [ValidFragment] 根據后面的log提示是由于重載了fragment的構造函數&…

迅雷影音怎樣 1.5倍速度播放

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 看視頻 覺得播放速度太慢&#xff0c;想讓1.5速度播放可以這樣設置&#xff1a; 點擊快進按鈕&#xff0c;點一次變為1.1倍&#xff0c…

【Java】Mybatis mapper動態代理方式

前言 我們在使用Mybatis的時候&#xff0c;獲取需要執行的SQL語句的時候&#xff0c;都是通過調用xml文件來獲取&#xff0c;例如&#xff1a;User user (User) sqlSession.selectOne("cn.ddnd.www.Entity.User.getUser", "xue8qq.com");。這種方式是通過…

git pull時沖突的幾種解決方式

僅結合本人使用場景&#xff0c;方法可能不是最優的 1. 忽略本地修改&#xff0c;強制拉取遠程到本地 主要是項目中的文檔目錄&#xff0c;看的時候可能多了些標注&#xff0c;現在遠程文檔更新&#xff0c;本地的版本已無用&#xff0c;可以強拉 git fetch --allgit reset --h…

Android應用開發—eventBus發布事件和事件處理的時序關系

占坑&#xff0c;簡單說明下eventBus發布事件和事件處理的時序關系。 什么時候使用sticky&#xff1a; 當你希望你的事件不被馬上處理的時候&#xff0c;舉個栗子&#xff0c;比如說&#xff0c;在一個詳情頁點贊之后&#xff0c;產生一個VoteEvent&#xff0c;VoteEvent并不立…

grep命令 解說

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 grep&#xff08;global search regular expression(RE) and print out the line&#xff0c;全面搜索正則表達式并把行打印出來&#x…

創業第一桶金怎么來

文章摘要&#xff1a;資金是創業要具備的一個必要條件&#xff0c;那么對于創業者來說&#xff0c;第一桶金如何取得&#xff1f;資金是創業要具備的一個必要條件&#xff0c;那么對于創業者來說&#xff0c;第一桶金如何取得&#xff1f;   一、一門手藝   都說擁有萬貫…

4001.基于雙向鏈表的雙向冒泡排序法

基于雙向鏈表的雙向冒泡排序法 發布時間: 2018年11月26日 10:09 時間限制: 1000ms 內存限制: 128M 習題集源碼中出現了 temp->next->prior p; 本人推斷這里缺少預先的對temp->nextNULL這種情況的判定&#xff0c;所以需加入一個判斷語句解決。 此為非循環的雙向鏈…

頁面向上滾動

#頁面或者div向上無縫滾動 1.css: body {margin: 0;padding: 0;overflow: hidden;}.container {position: relative;top: 0;}.container div {width: 500px;height: 500px;border: 1px solid chartreuse;font-size: 100px;line-height: 500px;font-weight: bold;color: black;t…

叨逼叨

此處記錄點零散的小idea&#xff0c;為了避免把csdn當微博&#xff0c;開一篇&#xff0c;都記在這里吧。 感覺服務注冊機制&#xff0c;貌似也是一種依賴注入。&#xff08;雖然我還沒完全搞懂依賴注入&#xff09;&#xff0c;理由呢&#xff1a;你需要一個模塊的功能&#x…

Linux:echo命令詳解

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 echo命令 用于字符串的輸出 格式 echo string使用echo實現更復雜的輸出格式控制 1.顯示普通字符串: echo "It is a test"這里…

看年輕人如何賺第一桶金

上世紀90年代&#xff0c;成為百萬富翁&#xff0c;對很多人只是個夢想。不過如今&#xff0c;隨著經濟飛速發展&#xff0c;擁有百萬資產已經不再是神話&#xff0c;放眼望去&#xff0c;我們身邊的百萬富翁比比皆是&#xff0c;甚至很多初入社會、白手起家的年輕人&#xff0…

跨越解決方案之nginx

這里是修真院前端小課堂&#xff0c;每篇分享文從 【背景介紹】【知識剖析】【常見問題】【解決方案】【編碼實戰】【擴展思考】【更多討論】【參考文獻】 八個方面深度解析前端知識/技能&#xff0c;本篇分享的是&#xff1a; 【跨越解決方案之nginx】 1.背景介紹 跨域&#x…

學習 shell腳本之前的基礎知識

見 : http://www.92csz.com/study/linux/12.htm【什么是shell】 簡單點理解&#xff0c;就是系統跟計算機硬件交互時使用的中間介質&#xff0c;它只是系統的一個工具。實際上&#xff0c;在shell和計算機硬件之間還有一層東西那就是系統內核了。打個比方&#xff0c;如果把計算…