用途
變量的解構(gòu)賦值用途很多。
(1)交換變量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
上面代碼交換變量x和y的值,這樣的寫法不僅簡潔,而且易讀,語義非常清晰。
(2)從函數(shù)返回多個(gè)值
函數(shù)只能返回一個(gè)值,如果要返回多個(gè)值,只能將它們放在數(shù)組或?qū)ο罄锓祷亍S辛私鈽?gòu)賦值,取出這些值就非常方便。
// 返回一個(gè)數(shù)組
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一個(gè)對(duì)象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
(3)函數(shù)參數(shù)的定義
解構(gòu)賦值可以方便地將一組參數(shù)與變量名對(duì)應(yīng)起來。
// 參數(shù)是一組有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 參數(shù)是一組無次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
(4)提取 JSON 數(shù)據(jù)
解構(gòu)賦值對(duì)提取 JSON 對(duì)象中的數(shù)據(jù),尤其有用。
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]
上面代碼可以快速提取 JSON 數(shù)據(jù)的值。
(5)函數(shù)參數(shù)的默認(rèn)值
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
} = {}) {
// ... do stuff
};
指定參數(shù)的默認(rèn)值,就避免了在函數(shù)體內(nèi)部再寫var foo = config.foo || 'default foo';這樣的語句。
(6)遍歷 Map 結(jié)構(gòu)
任何部署了 Iterator 接口的對(duì)象,都可以用for...of循環(huán)遍歷。Map 結(jié)構(gòu)原生支持 Iterator 接口,配合變量的解構(gòu)賦值,獲取鍵名和鍵值就非常方便。
const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is hello
// second is world
如果只想獲取鍵名,或者只想獲取鍵值,可以寫成下面這樣。
// 獲取鍵名
for (let [key] of map) {
// ...
}
// 獲取鍵值
for (let [,value] of map) {
// ...
}
(7)輸入模塊的指定方法
加載模塊時(shí),往往需要指定輸入哪些方法。解構(gòu)賦值使得輸入語句非常清晰。
const { SourceMapConsumer, SourceNode } = require("source-map");
主要知識(shí)點(diǎn):
1. 解構(gòu)不成功的話 返回值為 undefined
2. 解構(gòu)賦值的規(guī)則是,只要等號(hào)右邊的值不是對(duì)象或數(shù)組,就先將其轉(zhuǎn)為對(duì)象。由于undefined和null無法轉(zhuǎn)為對(duì)象,所以對(duì)它們進(jìn)行解構(gòu)賦值,都會(huì)報(bào)錯(cuò)。
1.字符串也可以解構(gòu)賦值。這是因?yàn)榇藭r(shí),字符串被轉(zhuǎn)換成了一個(gè)類似數(shù)組的對(duì)象,類似數(shù)組的對(duì)象都有一個(gè)length屬性,因此還可以對(duì)這個(gè)屬性解構(gòu)賦值。
2.解構(gòu)賦值時(shí),如果等號(hào)右邊是數(shù)值和布爾值,則會(huì)先轉(zhuǎn)為對(duì)象。
3. 默認(rèn)值:ES6 內(nèi)部使用嚴(yán)格相等運(yùn)算符(===),判斷一個(gè)位置是否有值。所以,只有當(dāng)一個(gè)數(shù)組成員嚴(yán)格等于undefined,默認(rèn)值才會(huì)生效。
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
上面代碼中,如果一個(gè)數(shù)組成員是null,默認(rèn)值就不會(huì)生效,因?yàn)閚ull不嚴(yán)格等于undefined。
如果默認(rèn)值是一個(gè)表達(dá)式,那么這個(gè)表達(dá)式是惰性求值的,即只有在用到的時(shí)候,才會(huì)求值。
function f() {
console.log('aaa');
}
let [x = f()] = [1];
上面代碼中,因?yàn)閤能取到值,所以函數(shù)f根本不會(huì)執(zhí)行。上面的代碼其實(shí)等價(jià)于下面的代碼。
let x;
if ([1][0] === undefined) {
x = f();
} else {
x = [1][0];
}
4. 對(duì)象的解構(gòu)賦值的內(nèi)部機(jī)制,是先找到同名屬性,然后再賦給對(duì)應(yīng)的變量。真正被賦值的是后者,而不是前者。
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined
上面代碼中,foo是匹配的模式,baz才是變量。真正被賦值的是變量baz,而不是模式foo。
注意點(diǎn):
(1)如果要將一個(gè)已經(jīng)聲明的變量用于解構(gòu)賦值,必須非常小心。
// 錯(cuò)誤的寫法
let x;
{x} = {x: 1};
// SyntaxError: syntax error
上面代碼的寫法會(huì)報(bào)錯(cuò),因?yàn)?JavaScript 引擎會(huì)將{x}理解成一個(gè)代碼塊,從而發(fā)生語法錯(cuò)誤。只有不將大括號(hào)寫在行首,避免 JavaScript 將其解釋為代碼塊,才能解決這個(gè)問題。
// 正確的寫法
let x;
({x} = {x: 1});
上面代碼將整個(gè)解構(gòu)賦值語句,放在一個(gè)圓括號(hào)里面,就可以正確執(zhí)行。關(guān)于圓括號(hào)與解構(gòu)賦值的關(guān)系,參見下文。
(2)解構(gòu)賦值允許等號(hào)左邊的模式之中,不放置任何變量名。因此,可以寫出非常古怪的賦值表達(dá)式。
({} = [true, false]);
({} = 'abc');
({} = []);
上面的表達(dá)式雖然毫無意義,但是語法是合法的,可以執(zhí)行。
(3)由于數(shù)組本質(zhì)是特殊的對(duì)象,因此可以對(duì)數(shù)組進(jìn)行對(duì)象屬性的解構(gòu)。
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
上面代碼對(duì)數(shù)組進(jìn)行對(duì)象解構(gòu)。數(shù)組arr的0鍵對(duì)應(yīng)的值是1,[arr.length - 1]就是2鍵,對(duì)應(yīng)的值是3。方括號(hào)這種寫法,屬于“屬性名表達(dá)式”
5.圓括號(hào)問題
解構(gòu)賦值雖然很方便,但是解析起來并不容易。對(duì)于編譯器來說,一個(gè)式子到底是模式,還是表達(dá)式,沒有辦法從一開始就知道,必須解析到(或解析不到)等號(hào)才能知道。
由此帶來的問題是,如果模式中出現(xiàn)圓括號(hào)怎么處理。ES6 的規(guī)則是,只要有可能導(dǎo)致解構(gòu)的歧義,就不得使用圓括號(hào)。
但是,這條規(guī)則實(shí)際上不那么容易辨別,處理起來相當(dāng)麻煩。因此,建議只要有可能,就不要在模式中放置圓括號(hào)。