解構賦值(destructuring assignment)語法是一個Javascript表達式,這種語法可以更方便的提取出 Object 或者 Array 中的數據。這種語法能夠在接受提取的數據的地方使用,好比一個表達式的左邊。有明確的語法模式來告訴咱們如何使用這種語法提取須要的數據值。html
解構 Object:es6
const obj = { first: 'Jane', last: 'Doe' }; const {first: f, last: l} = obj; // f = 'Jane'; l = 'Doe' // {prop} is short for {prop: prop} const {first, last} = obj; // first = 'Jane'; last = 'Doe'
解構能幫助更好地處理方法返回的對象:數組
const obj = { foo: 123 }; const {writable, configurable} = Object.getOwnPropertyDescriptor(obj, 'foo'); console.log(writable, configurable); // true true
解構數組,對全部可遍歷的值有效。bash
let foo = ["one", "two", "three"]; let [one, two, three] = foo; console.log(one); // "one" console.log(two); // "two" console.log(three); // "three" const iterable = ['a', 'b']; const [x, y] = iterable; // x = 'a'; y = 'b' [x, y] = iterable; // window.x = 'a'; window.y = 'b';
一樣的,解構數組也能幫助咱們更好地處理函數返回值:app
const [all, year, month, day] = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ .exec('2999-12-31');
並且,你也能夠忽略你不感興趣的返回值:函數
function f() { return [1, 2, 3]; } let [a, , b] = f(); console.log(a); // 1 console.log(b); // 3
你也能夠忽略所有返回值,不過彷佛沒啥用:ui
[,,] = f();
當解構一個數組時,可使用剩餘模式(拓展語句,Spread operator),將數組剩餘部分賦值給一個變量。prototype
let [a, ...b] = [1, 2, 3]; console.log(a); // 1 console.log(b); // [2, 3]
解構能夠在下面這些情景中使用,只展示了數組模式的演示,對象模式也是如此。rest
// 變量聲明: const [x] = ['a']; let [x] = ['a']; var [x] = ['a']; // 賦值: 下面這種狀況將會在全局變量上添加一個 x 屬性,值爲‘a‘ [x] = ['a']; // 參數的定義: function userId({id}) { return id; } function whois({displayName: displayName, fullName: {firstName: name}}){ console.log(displayName + " is " + name); } var user = { id: 42, displayName: "jdoe", fullName: { firstName: "John", lastName: "Doe" } }; console.log("userId: " + userId(user)); // "userId: 42" whois(user); // "jdoe is John" function f([x]) { ··· } f(['a']);
也能夠在 for-of 循環中使用:code
const arr = ['a', 'b']; for (const [index, element] of arr.entries()) { console.log(index, element); } // Output: // 0 a // 1 b
在解構中,有下面兩部分參與:
Destructuring source: 解構的源,將要被解構的數據,好比解構賦值表達式的右邊部分。
Destructuring target: 解構的目標,好比解構複製表達式的左邊部分。
解構的目標能夠是下面三個的任意一個:
賦值對象,Assigment Patterns。例如 x
賦值對象一般來講是一個變量。可是在解構賦值中,你有更多的選擇,稍後會講到。
對象模型,Object Patterns。好比:{ first: «pattern», last: «pattern» }
數組模型,Object Patterns。好比:[ «pattern», «pattern» ]
能夠任意嵌套模型,並且是能夠很是任性的嵌套。
const obj = { a: [{ foo: 123, bar: 'abc' }, {}], b: true }; const { a: [{foo: f}] } = obj; // f = 123
在一個表達式pattern = someValue
中,pattern
是如何訪問someValue
的呢?
在訪問屬性以前,object pattern 將解構的源數據(destructuing source)轉換成對象。
const {length : len} = 'abc'; // len = 3 const {toString: s} = 123; // s = Number.prototype.toString
在這個過程當中,強制轉換成對象的過程不是經過Object()
方法,而是經過內置的操做方法toObject()。這兩個操做處理undefined
和null
的方式不太同樣。
Object()方法將原始類型值轉換成包裝類型對象(wrapper object),原來的值原封不動。
> typeof Object('abc') 'object' > var obj = {}; > Object(obj) === obj true
也會將undefined
和 null
轉換成一個空的對象。
> Object(undefined) {} > Object(null) {}
對比之下,當遇到undefined
和null
的時候,toObject()方法則會拋出一個錯誤。因此下面的解構是失敗的:
const { prop: x } = undefined; // TypeError const { prop: y } = null; // TypeError
所以,你可使用空對象模型{}來檢查一個值是否被強制轉換成了一個對象。正如前面提到的規則,undefined
和null
將會拋出錯誤
({} = [true, false]); // OK, Arrays are coercible to objects ({} = 'abc'); // OK, strings are coercible to objects ({} = undefined); // TypeError ({} = null); // TypeError
表達式兩邊的括號是必須的,由於在 JavaScript 中,聲明不能以花括號開始。
數組解構使用一個迭代器來獲取數據源中的元素。所以,你能夠對任何能夠遍歷的值使用數組解構。
字符串是可遍歷的:
const [x, ...y] = 'abc'; // x='a'; y=['b', 'c']
咱們沒法經過索引訪問 Set中的元素,可是能夠經過迭代器。因此,數組解構可以在 Sets上工做:
const [x,y] = new Set(['a', 'b']); // x='a'; y='b’;
Set
的迭代器老是按照元素插入的順序將元素返回,因此上述的解構返回的結果老是相同的。
若是一個值有一個 key 爲Symbol.iterator
的方法,這個方法返回的是一個對象,那麼這個值是能夠遍歷的。若是被解構的值不能遍歷的,那麼「數組解構」會拋出一個TypeError
錯誤。
let x; [x] = [true, false]; // OK, Arrays are iterable [x] = 'abc'; // OK, strings are iterable [x] = { * [Symbol.iterator]() { yield 1 } }; // OK, iterable [x] = {}; // TypeError, empty objects are not iterable [x] = undefined; // TypeError, not iterable [x] = null; // TypeError, not iterable
能夠用一個空的數組模型[]來檢查值是否是可遍歷的:
[] = {}; // TypeError, empty objects are not iterable [] = undefined; // TypeError, not iterable [] = null; // TypeError, not iterable
默認值是可選的,在數據源中找不到對應的值時,若是設置了默認值,則匹配這個默認值做爲匹配結果,不然返回 undefined。
const [x=3, y] = []; // x = 3; y = undefined。 const {foo: x=3, bar: y} = {}; // x = 3; y = undefined
當解構模式有匹配結果,且匹配結果是 undefined 時,也會使用默認值做爲返回結果:
const [x=1] = [undefined]; // x = 1 const {prop: y=2} = {prop: undefined}; // y = 2
也就是說下面的解構:
const {prop: y=someFunc()} = someValue;
至關於:
let y; if (someValue.prop === undefined) { y = someFunc(); } else { y = someValue.prop; }
使用console.log()
能夠觀察到:
> function log(x) { console.log(x); return 'YES' } > const [a=log('hello')] = []; > a 'YES' > const [b=log('hello')] = [123]; > b 123
在第二個解構中,默認值沒有觸發,而且log()
沒有被調用。
默認值能夠引用模式中的任何變量,包括相同模式中的其餘變量:
const [x=3, y=x] = []; // x=3; y=3 const [x=3, y=x] = [7]; // x=7; y=7 const [x=3, y=x] = [7, 2]; // x=7; y=2
可是,變量的順序很關鍵,從左到右,先聲明的變量不能引用後聲明的變量,也就是左邊的不能引用右邊的。
const [x=y, y=3] = []; // ReferenceError
到目前爲止,咱們所看到的都是模式中變量的默認值,咱們也能夠爲模式設置默認值。
const [{prop: x} = {}] = [];
若是整個模式沒有匹配結果,則使用{}
做爲數據源來匹配。
const { prop: x } = {}; // x = undefined
上面的例子中,x 爲 undefined 可能仍是不夠直觀。看下面這個例子:
const [{prop: x} = {props: 'abc'}] = []; // x=abc
若是屬性值是一個變量,和屬性的 key相同,就能夠忽略這個 key:
const { x, y } = { x: 11, y: 8 }; // x = 11; y = 8 // 等價於 const { x: x, y: y } = { x: 11, y: 8 };
若是把表達式放入方括號中,能夠用這個表達式聲明屬性的鍵:
const FOO = 'foo'; const { [FOO]: f} = {fooL 123}; // f = 123
這也使得可使用 symbols 來作屬性的鍵:
// Create and destructure a property whose key is a symbol const KEY = Symbol(); const obj = { [KEY]: 'abc' }; const { [KEY]: x } = obj; // x = 'abc' // Extract Array.prototype[Symbol.iterator] const { [Symbol.iterator]: func } = []; console.log(typeof func); // function
在解構的過程當中能夠跳過一些元素:
const [,,x,y] = [1,2,3,4]; // x= 3 y = 4;
剩餘運算符能夠將一個可遍歷對象中剩餘的元素提取到一個數組中。若是這個運算符在數組模式中使用,運算符必須放在最後:
const [x, ...y] = [1,2,3,4]; // x=1; y=[2,3,4];
要注意的時,拓展運算符(spread operator)與剩餘操做符有着相同的語法-三個點。可是它們之間有區別:前者將數組變成多個元素;後者則用來解構和提取數據,多個元素壓縮成一個元素。
若是運算符找不到任何元素,將會匹配一個空的數組,永遠不會返回undefined 或者 null。例如:
const [x, y, ...z] = ['a']; // x='a'; y=undefined; z
操做符不必定非要是一個變量,也可使用模式:
const [x, ...[y, z]] = ['a', 'b', 'c']; // x = 'a'; y = 'b'; z = 'c'
在使用解構的時候,有兩點要考慮清楚:
不能使用大括號做爲聲明語句的開頭;
在解構的過程當中,能夠申明變量或者分配給變量,可是不能同時這麼作;
在 for-of 中使用解構:
const map = new Map().set(false, 'no').set(true, 'yes'); for (const [key, value] of map) { console.log(key + ' is ' + value); }
使用解構交換兩個變量的值:
[a, b] = [b, a];
或者:
[a, b, c] = [c, a, b];
還能夠分割數據:
const [first, ...rest] = ['a', 'b', 'c']; // first = 'a'; rest = ['b', 'c']
處理方法返回的數組更加方便:
const [all, year, month, day] = /^(\d\d\d\d)-(\d\d)-(\d\d)$/.exec('2999-12-31'); const cells = 'Jane\tDoe\tCTO' const [firstName, lastName, title] = cells.split('\t'); console.log(firstName, lastName, title);
要注意的一點是:exec等一些方法可能會返回 null,致使程序拋出錯誤TypeError
,此時須要添加一個默認值:
const [, year, month, day] = /^(\d\d\d\d)-(\d\d)-(\d\d)$/.exec(someStr) || [];
參考資料: