ES6 - 變數的解構賦值解析

王小端coder發表於2019-03-31
  • ES6 允許按照一定模式,從陣列和物件中提取值,對變數進行賦值,這被稱為解構(Destructuring)。
  • 如果解構不成功,變數的值就等於undefined。

陣列的解構賦值

例子:

let [temA, temB, temC] = ['AAAA', 'BBBB', 'CCCC']
console.log(temA); // AAAA
console.log(temB); // BBBB
console.log(temC); // CCCC
複製程式碼

上面程式碼表示,可以從陣列中提取值,按照對應位置,對變數賦值。本質上這種寫法屬於‘模式匹配‘,只要等號兩邊的模式相同,左邊的變數就會被賦予對應的值。下面看些複雜的例子:

let [temA, [temB, temC], temD] = ['AAAA', ['BBBB', 'CCCC'], 'DDDD'];
console.log(temA); // AAAA
console.log(temB); // BBBB
console.log(temC); // CCCC
console.log(temD); // DDDD

let [ , , temA] = ['AAAA', 'BBBB', 'CCCC'];
console.log(temA); // CCCC

let [temA, temB, temC] = ['AAAA'];
console.log(temC); // undefined


複製程式碼

解構一般有三種情況,1.完全解構;2.不完全解構;3.解構不成功。

在上述例子中存在完全解構和解構不成功的例子,下面來看一下不完全解構的例子

let [temA, temB] = ['AAAA', 'BBBB', 'CCCC'];
console.log(temA); // AAAA
console.log(temB); // BBBB

let [temA, [temB], temD] = ['AAAA', ['BBBB','CCCC'], 'DDDD'];
console.log(temA); // AAAA
console.log(temB); // BBBB
console.log(temD); // DDDD
複製程式碼

不完全解構:即等號左邊的模式,只匹配一部分的等號右邊的陣列,這種情況下解構依然成功。

如果等號的右邊不是陣列,或者說不是可遍歷的結構,那麼將會報錯,看下面的例子

let [temA] = 1;
let [temB] = false;
let [temC] = NaN;
let [temD] = undefined;
let [temE] = null;
let [temF] = {};
複製程式碼

上面的語句都會報錯:Uncaught TypeError: undefined is not a function

  • 解構賦值允許指定預設值
let [temA, temB = 'bbbb'] = ['AAAA'];
console.log(temB); // bbbb

let [temA, temB = 'bbbb'] = ['AAAA', undefined];
console.log(temB); // bbbb
        
let [temA, temB = 'bbbb'] = ['AAAA', null];
console.log(temB); // null
複製程式碼

陣列成員為undefined時,預設值仍會生效;如果一個陣列成員是null,預設值就不會生效,因為null不嚴格等於undefined。

物件的解構賦值

物件的解構與陣列有一個重要的不同,陣列的元素是按次序排列的,變數的取值由它的位置決定;而物件的屬性沒有次序,變數必須與屬性同名,才能取到正確的值。

let {temA, temB} = {temA: 'AAAA', temB: 'BBBB'};
console.log(temA); // AAAA
console.log(temB); // BBBB
// 上面例子與下面例子等同
let {temA: temA, temB: temB} = {temA: 'AAAA', temB: 'BBBB'};
console.log(temA); // AAAA
console.log(temB); // BBBB
複製程式碼

物件的解構賦值的內部機制,是先找到同名屬性,然後再賦值給對應的變數,真正被賦值的是後者,而不是前者。

如果變數名和屬性名不一致,必須寫成下面例子這樣

let {temA: temG} = {temA: 'AAAA', temB: 'BBBB'}
console.log(temG); // AAAA

let obj = {temA: 'AAAA', temB: 'BBBB'};
let {temA: A, temB: B} = obj;
console.log(A); // AAAA
console.log(B); // BBBB
複製程式碼

與陣列一樣,解構也可以用於巢狀解構的物件,看下面例子

let obj = {
    data: [
        'temA',
        {temB: 'BBBB'}
    ]
};
let {data: [temA, {temB}]} = obj;
console.log(temA); // temA
console.log(temB); // BBBB
複製程式碼
const node = {
    res: {
        parent: {
            num: 1,
            content: 'Content'
        }
    }
}

let {res, res: {parent}, res: {parent: {num}}} = node;
console.log(res); // Object {parent: Object}
console.log(parent); // Object {num: 1, content: "Content"}
console.log(num); // 1
複製程式碼

上面程式碼有三次解構賦值,分別是對res、parent、num三個屬性的解構賦值。

注意,最後一次對num屬性的解構賦值之中,只有num是變數,res和parent都是模式,不是變數。

如果解構模式是巢狀的物件,而且子物件所在的父屬性不存在,那麼將會報錯。

let {node: {temA}} = {temB: 'temC'};
// Uncaught TypeError: Cannot match against 'undefined' or 'null'.
複製程式碼
  • 物件的解構也能制定預設值
var {temA = 'AAAA'} = {};
console.log(temA); // AAAA
        
var {temA, temB = 'BBBB'} = {temA: 'AAAA'};
console.log(temA); // AAAA
console.log(temB); // BBBB
        
var {temA: temB = 'BBBB'} = {};
console.log(temB); // BBBB
        
var {temA: temB = 'BBBB'} = {temA: 'AAAA'};
console.log(temB); // AAAA
複製程式碼

字串的解構賦值

字串也可以解構賦值,在解構賦值時會被轉換成類似的陣列物件。其中還有一個length屬性,因此還可以對這個屬性解構賦值。

const [temA, temB, temC, temD, temE] = 'world';
console.log(temA); // w
console.log(temB); // o
console.log(temC); // r
console.log(temD); // l
console.log(temE); // d
        
let {length: len} = 'world';
console.log(len); // 5
複製程式碼

數值的解構賦值

數值在解構賦值時,會先轉為物件。解構賦值的規則是,只要等號右邊的值不是物件或陣列,就先將其轉為物件。

let {temA: temB} = 123;
console.log(temB === Number.prototype.temA);
複製程式碼

布林值的解構賦值

布林值在解構賦值時會和數值一樣,先轉為物件。

let {temA: temB} = true;
console.log(temB === Number.prototype.temA);
複製程式碼

函式引數的解構賦值

函式的引數也可以使用解構賦值

function add([argA, argB]){
    return argA + argB;
}

console.log(add([1, 2])); // 3
複製程式碼

函式引數的解構也可以使用預設值

function add([argA, argB = 5]){
    return argA + argB;
}
console.log(add([1])); // 6

function add({argA, argB = 5} = {}){
    return argA + argB;
}
console.log(add({argA: 1})); // 6
複製程式碼

變數解構賦值的用途

  1. 交換變數的值
let temA = 'AAAA';
let temB = 'BBBB';
[temA, temB] = [temB, temA];
console.log(temA); // BBBB
console.log(temB); // AAAA
複製程式碼
  1. 從函式返回多個值
function Fn(){
    return [1, 2, 3];
}
let [temA, temB, temC] = Fn();
// 返回一個陣列
        
function Fn(){
    return {
        temA: 'AAAA',
        temB: 'BBBB'
    }
}
let {temA, temB} = Fn();
// 返回一個物件
複製程式碼
  1. 函式引數的定義
function Fn([temA, temB, temC]){
    console.log(temA + temB + temC);
}
Fn([1, 2, 3]);
複製程式碼
  1. 提取JSON資料
let jsonData = {
    id: 1,
    name: 'Xiao Duan',
    data: [22, 99]
}

let {id, name, data: number} = jsonData;

console.log(id, name, number); // 1 "Xiao Duan" [22, 99]
        
複製程式碼
  1. 函式引數的預設值
jQuery.ajax = function (url, {
    async = true,
    beforeSend = function () {},
    cache = true,
    complete = function () {},
    crossDomain = false,
    global = true,
    // ... more config
} = {}) {
    // ... do stuff
};
複製程式碼
  1. 遍歷Map結構
const map = new Map();
map.set('temA', 'hello');
map.set('temB', 'world');
for (let [key, value] of map){
    console.log(key + " is " + value);
}
複製程式碼
  1. 輸入模組的制定方法 載入模組時,往往需要指定輸入哪些方法。解構賦值使得輸入語句非常清晰。
const { SourceMapConsumer, SourceNode } = require("source-map");複製程式碼


相關文章