本次我領到的任務是:html
在ES6中有一個解構賦值運算符,能夠大大方便數據字段的獲取。 好比 const [a, b] = [1, 2, 3]; const {name, age} = {name: 'helijia', age: 3}; 上面的語句是咱們經常使用的,但是你能解釋爲何下面的語句也能正常工做嗎? const [a, b] = 'abc'; const {toString: s} = 123; 任務: 1. 解釋上面兩個解構語句的工做原理 2. 你可以實現自定義類的數組解構嗎? 好比: class A = … const a = new A(); const [e1, e2] = a; // 怎麼樣才能讓自定義的類也能支持支持數組的解構呢?
ES5時,處理默認值的慣用法:node
const scale = opts.scale || 1;
如今能夠:git
const {scale = 1} = opts;
不過二者並不等價,默認值只會在目標值是undefined時纔會生效。es6
const {scale = 1} = {} // scale === 1 const {scale = 1} = {scale: null} // scale === null const {scale = 1} = {scale: undefined} // scale === 1
有個技巧是能夠在一條語句內實現變量值的交換。github
原來須要三句話:正則表達式
var tmp = a; var a = b; var b = a;
如今只須要:算法
const [a, b] = [b, a];
這讓咱們在實現一些基礎算法時更加精練。chrome
在ES5中函數只能返回一個值,有了解構賦值,能夠模擬出多個返回值。c#
const [a, b] = f();
固然從設計上說,js的函數的返回值仍是應該是單一的模型比較合適。 數組
很小看到接口層面返回一個數組做爲多個值。 可能在編寫一些專業領域或DSL應用時會用獲得。
而在實現時常常會使用解構賦值帶來的便利:
const {name, age} = getInfo();
const [a, , c] = [1, 2, 3]; // a === 1 // c === c
我想到的一個應用是一會兒從正則表達式match對象中取出多個元素。
const re = /^([^=]+)=(.*)$/; const [, key, value] = re.exec('name=helijia'); // key === 'name' // value === 'helijia'
解構賦值運算符配合spread會比較有用。
const {name, age, ...exts} = item; return <Item {...exts} />;
exts對象中並不包含name和age,若是在ES5中要費好幾句語句。
數組也支持spread,可是數組自己具備slice等函數,因此通常用不上。
var [head, ...tail] = [1, 2, 3, 4]; console.log(tail); // [2, 3, 4]
對象的解構還支持從新命名,這在名字衝突,或者簡化代碼時會比較有用。
const item = { artisanNick: '玉米' artisanLevel: 10, artisanType: 3 }; const {artisanNick:nick, artisanLevel:level, artisanType:type} = item;
原來咱們寫成
const nick = item.artisanNick; const level = item.artisanLevel; const type = item.artisanType;
可配合默認值一塊兒用:
const {a:aa = 10, b:bb = 5} = {a: 3}
這在實際開發中就用的比較多了,好比在React組件開發中:
const Product = ({name, price}) => ( <div> <div>name: {name}</div> <div>price: {price}</div> </div> );
以上描述的特性使用babel編譯器就能在主流瀏覽器中工做,babel對ES6的支持是經過將代碼編譯成ES5代碼來實現的;
而nodejs和chrome原生是直接支持es6的,它們是基於V8引擎在解釋器層面支持ES6,所以二者能力是有差別的。
經過babel編譯的ES6,最後本質是ES5代碼,是靜態的,因此只能支持一些語法糖的功能;
下面是一些示例:
// ES6 const [a, b, c, d] = [1, 2, 3, 4]; // 對應的ES5 var a = 1; var b = 2; var c = 3; var d = 4;
// ES6 const list = [1, 2, 3, 4]; const [a, b, c, d] = list; // ES5 var list = [1, 2, 3, 4]; var a = list[0]; var b = list[1]; var c = list[2]; var d = list[3];
// ES6 const {a:aa = 10, b:bb = 5} = {a: 3} // ES5 var _a = { a: 3 }; var _a$a = _a.a; var aa = _a$a === undefined ? 10 : _a$a; var _a$b = _a.b; var bb = _a$b === undefined ? 5 : _a$b;
重命名和默認值的處理。
// ES6 const [a, b, c] = '1234'; // ES5 var _ = '1234'; var a = _[0]; var b = _[1]; var c = _[2];
字符串也當成數組同樣處理了,因此恰好正常工做。
其實只要實現迭代器接口,就可以解構。
const set = new Set([1, 2, 3, 4]) const [a, b, c] = set; console.log(a, b, c); // 1 2 3
這段代碼在chrome的cosnole和nodejs中都能正常工做,不過在babel中就歇菜了。
由於它編譯後的結果爲:
var set = new Set([1, 2, 3, 4]); var a = set[0]; var b = set[1]; var c = set[2]; console.log(a, b, c); // undefined undefined undefined
固然Map也是實現了迭代器接口的。
const map = new Map(); map.set('window', window); map.set('document', document); for (const [key, value] of map) { console.log(key + " is " + value); } const [[k1, v1], [k2, v2]] = map; // destructring
再來一個例子:
function* iter() { yield 1; yield 2; yield 3; } const [a, b, c] = iter(); console.log(a, b, c); // 1 2 3
一樣這段代碼在babel中也不能正常工做。
const [a, b] = 'abc'; const {toString: s} = 123; 任務: 1. 解釋上面兩個解構語句的工做原理 2. 你可以實現自定義類的數組解構嗎?
因此以上兩個語句能正常工做,緣由是分場景的,在經過babel編譯成ES5和經過解釋器直接執行原理是不同的。
babel編譯器會把它編譯成
// ES6 const [a, b, c] = '1234'; const {toString: s} = "123"; // ES5 var _ = '1234'; var a = _[0]; var b = _[1]; var c = _[2]; var _2 = "123"; var s = _2.toString;
而js引擎執行ES6是由於字符串實現了迭代器接口,以及支持對象屬性訪問。
對於第2個問題,咱們可讓自定義類實現迭代器接口來支持,只是在babel中不能正常工做。
如下是一個示例:
class Random { [Symbol.iterator]() { return { next() { return {value: Math.random(), done: false}; } } } } const random = new Random(); for (const n of random) { if (n > 0.8) { break; } console.log(n); } const [e1, e2, e3, e4] = new Random(); console.log(e1, e2, e3, e4);
看到數組的解構處理,第一步老是取得一個迭代器,而後操做這個迭代器。
從規範中知道,解構賦值操做符對應的元素就是 DestructuringAssignment
,查詢V8代碼可知,
V8在parser階段就會把解構賦值語句重寫成等效的賦值語句, 這樣解釋器不須要作修改就能夠運行新的語法,也保證了效率。
關鍵代碼片斷:
使用了近一年半的Elixir,有許多語言特性另人着迷,其中模式匹配就是一個。
在ES6中引入了和模式匹配語法有點接近的解構賦值(Destructring Assigmnent)語句,可是僅僅是部分精簡代碼的語法糖,而在語義和表達上並無本質變化。
不過搜索github上,看到已有相關的proposal,以及babel實現的issue,因此藉此機會熟悉瞭解一番。
另外發現一個js庫js-pattern-matching,提供了一個函數來支持模式匹配
其中這個JS庫在不引入新語法特性的基礎上支持較好的模式匹配語法,我以爲挺讚的。 它的原理是利用function.toString,獲得函數字符串,再生成匹配的新的函數。
我寫了幾個簡單的示例試用了一下,感受還不錯,不過在類型匹配時有些BUG。
const match = require('js-pattern-matching'); const sum = (list) => match(list) ( ([x,...xs]) => x + sum(xs), ([]) => 0 ); console.log(sum([])); console.log(sum([1, 2, 3, 4]));
由於要符合語法,因此加個前續v=
,文檔說是可改爲其餘字母。
const fibs = (n) => match(n) ( (v= 0) => 0, (v= 1) => 1, _ => fibs(n - 2) + fibs(n - 1) ); for (let i = 0; i < 10; i++) { console.log(fibs(i)); }
const type = (v) => match(v) ( (Array) => 'array', (Object) => 'object', _ => 'unknow' );
我也看了proposal的語法,感受風格和原來的js差別太大,設計成Expression,能夠在任何地方使用,可能會由於功能太強而致使濫用,反而起不到原來模式匹配優雅簡潔的目的。
其餘人的一些探索,不過這個語法不是很美觀。