1、數(shù)值的解構(gòu)賦值
默認(rèn)值
解構(gòu)賦值允許指定默認(rèn)值。
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
注意,ES6 內(nèi)部使用嚴(yán)格相等運算符(===),判斷一個位置是否有值。所以,只有當(dāng)一個數(shù)組成員嚴(yán)格等于undefined,默認(rèn)值才會生效。
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
上面代碼中,如果一個數(shù)組成員是null,默認(rèn)值就不會生效,因為null不嚴(yán)格等于undefined。
如果默認(rèn)值是一個表達(dá)式,那么這個表達(dá)式是惰性求值的,即只有在用到的時候,才會求值。
function f() {
console.log('aaa');
}
let [x = f()] = [1];
上面代碼中,因為x能取到值,所以函數(shù)f根本不會執(zhí)行。
2、對象的解構(gòu)賦值
簡介
對象的解構(gòu)與數(shù)組有一個重要的不同。數(shù)組的元素是按次序排列的,變量的取值由它的位置決定;而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
如果解構(gòu)失敗,變量的值等于undefined。
let {foo} = {bar: 'baz'};
foo // undefined
對象的解構(gòu)賦值,可以很方便地將現(xiàn)有對象的方法,賦值到某個變量。
// 例一
let { log, sin, cos } = Math;
// 例二
const { log } = console;
log('hello') // hello
上面代碼的例一將Math對象的對數(shù)、正弦、余弦三個方法,賦值到對應(yīng)的變量上,使用起來就會方便很多。例二將console.log賦值到log變量。
如果變量名與屬性名不一致,必須寫成下面這樣。
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
這實際上說明,對象的解構(gòu)賦值是下面形式的簡寫
let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };
也就是說,對象的解構(gòu)賦值的內(nèi)部機(jī)制,是先找到同名屬性,然后再賦給對應(yīng)的變量。真正被賦值的是后者,而不是前者。
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined
上面代碼中,foo是匹配的模式,baz才是變量。真正被賦值的是變量baz,而不是模式foo。
默認(rèn)值
var {x = 3} = {};
x // 3
var {x, y = 5} = {x: 1};
x // 1
y // 5
var {x: y = 3} = {};
y // 3
var {x: y = 3} = {x: 5};
y // 5
var { message: msg = 'Something went wrong' } = {};
msg // "Something went wrong"
默認(rèn)值生效的條件是,對象的屬性值嚴(yán)格等于undefined。
var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null
上面代碼中,屬性x等于null,因為null與undefined不嚴(yán)格相等,所以是個有效的賦值,導(dǎo)致默認(rèn)值3不會生效。
注意點
(1)如果要將一個已經(jīng)聲明的變量用于解構(gòu)賦值,必須非常小心。
// 錯誤的寫法
let x;
{x} = {x: 1};
// SyntaxError: syntax error
上面代碼的寫法會報錯,因為 JavaScript 引擎會將{x}理解成一個代碼塊,從而發(fā)生語法錯誤。只有不將大括號寫在行首,避免 JavaScript 將其解釋為代碼塊,才能解決這個問題。
// 正確的寫法
let x;
({x} = {x: 1});
3、字符串的解構(gòu)賦值
字符串也可以解構(gòu)賦值。這是因為此時,字符串被轉(zhuǎn)換成了一個類似數(shù)組的對象。
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
類似數(shù)組的對象都有一個length屬性,因此還可以對這個屬性解構(gòu)賦值。
let {length : len} = 'hello';
len // 5
4、數(shù)值和布爾值的解構(gòu)賦值
解構(gòu)賦值時,如果等號右邊是數(shù)值和布爾值,則會先轉(zhuǎn)為對象。
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
上面代碼中,數(shù)值和布爾值的包裝對象都有toString屬性,因此變量s都能取到值。
解構(gòu)賦值的規(guī)則是,只要等號右邊的值不是對象或數(shù)組,就先將其轉(zhuǎn)為對象。由于undefined和null無法轉(zhuǎn)為對象,所以對它們進(jìn)行解構(gòu)賦值,都會報錯。
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
5、函數(shù)參數(shù)的解構(gòu)賦值
函數(shù)的參數(shù)也可以使用解構(gòu)賦值。
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
上面代碼中,函數(shù)add的參數(shù)表面上是一個數(shù)組,但在傳入?yún)?shù)的那一刻,數(shù)組參數(shù)就被解構(gòu)成變量x和y。對于函數(shù)內(nèi)部的代碼來說,它們能感受到的參數(shù)就是x和y。
函數(shù)參數(shù)的解構(gòu)也可以使用默認(rèn)值。
function move({x = 0, y = 0} = {}) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
上面代碼中,函數(shù)move的參數(shù)是一個對象,通過對這個對象進(jìn)行解構(gòu),得到變量x和y的值。如果解構(gòu)失敗,x和y等于默認(rèn)值。
注意,下面的寫法會得到不一樣的結(jié)果。
function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]
上面代碼是為函數(shù)move的參數(shù)指定默認(rèn)值,而不是為變量x和y指定默認(rèn)值,所以會得到與前一種寫法不同的結(jié)果。