深入理解ES6 笔记五:解构

352 阅读5分钟
解构是一种打破数据结构,将其拆分为更小部分的过程。

解构用法

对象解构

对象解构的语法形式是再一个赋值操作符左边放置一个对象字面量。

let node = {
    type: 'id',
    name: 'foo'
}

let {type, name} = node;
console.log(type);      // id
console.log(name);      // foo

注意:如果使用var、let、const解构声明变量,则必须提供初始化程序(也就是等号右侧的值),否则会导致程序抛出语法错误↓

// 语法错误!
let {type, name};
var {type, name};
const {type, name};

解构赋值

我们同样可以在给变量赋值时使用解构语法

let node = {
    type: 'id',
    name: 'foo'
},
    type = 'name',
    name = 'id';

({type, name} = node;)
console.log(type);      // id
console.log(name);      // foo

注意: 一定要用一对小括号包裹解构赋值语句,Javascript引擎将一对开放的花括号是为一个代码块,而语法规定,代码块语句不允许出现在赋值语句左侧,添加小括号后可以将块语句转化为一个表达式,从而实现整个解构赋值的过程。
解构表达式的值与表达式右侧的值相等,在任何可以使用值的地方都可以使用解构赋值表达式(解构赋值表达式如果为null或undefined会导致程序抛出错误)

let node = {
    type: 'id',
    name: 'foo'
},
    type = 'name',
    name = 'id';
    
function outputInfo(value) {
    console.log(value === node);    // true
}

outputInfo({type, name} = node);
console.log(type);      // id
console.log(name);      // foo

默认值

使用解构赋值表达式时,如果制定的局部变量名称在对象中不存在,那么这个局部变量会被赋值为undefined。

let node = {
    type: 'id',
    name: 'foo'
};
let {type, name, value} = node;
console.log(type);      // id
console.log(name);      // foo
console.log(value);     // undefined

当指定的属性不存在时,可以随意定义一个默认值,在属性名称后面加个等号和相应的默认值即可。

let node = {
    type: 'id',
    name: 'foo'
};
let {type, name, value = true} = node;
console.log(type);      // id
console.log(name);      // foo
console.log(value);     // true

为非同名局部变量赋值

ES6可以使用不同命名的局部变量来存储对象属性的值,并且可以为其添加默认值。

let node = {
    type: 'id',
    name: 'foo'
};
let {type, name: localName, value: localValue = true} = node;
console.log(type);           // id
console.log(localName);      // foo
console.log(localValue);     // true

嵌套对象解构

let node = {
    type: 'id',
    name: 'foo',
    loc: {
        start:{
            line: 1,
            column: 1
        },
        end: {
            line: 2,
            column: 2
        }
    }
}

let {loc: {start, end: localEnd}} = node;
console.log(start.line);    // 1
console.log(start.column);  // 1
console.log(localEnd.line);    // 2
console.log(localEnd.column);  // 2

所有冒号前的标识符都代表在对象中的检索位置,其右侧为被赋值的变量名; 如果冒号后时花括号,则意味着要赋予的最终值嵌套在对象内部更深的层级中。

数组解构

数组解构也可用于赋值上下文,但不需要用小括号包裹表达式。

let colors = ['red', 'green', 'blur'],
    firstColor = 'black',
    secondColor = 'pink';
[firstColor, secondColor] = colors;
[, , thirdColor] = colors;
console.log(firstColor);    // red
console.log(secondColor);   // green
console.log(secondColor);   // blur

数组解构语法还有一个独特的用例:交换两个变量的值

let a = 1, b = 2;
[a, b] = [b, a];
console.log(a);     // 2
console.log(b);     // 1

默认值

也可以在数组解构赋值表达式中为数组中的任意位置添加默认值,当指定位置的属性不存在或其值为undefined时使用默认值。

let colors = ['red', 'green', 'blur'],
    firstColor = 'black',
    secondColor = 'pink';
[firstColor, secondColor, thirdColor, fourColor = 'white'] = colors;
console.log(firstColor);    // red
console.log(secondColor);   // green
console.log(fourColor);     // white

嵌套数组解构

在原有的数组模式中插入另一个数组模式,即可将解构过程深入到下一个层级。

let colors = ['red', ['green', 'black'], 'blur'];
let [firstColor, [secondColor]] = colors;
console.log(firstColor);    // red
console.log(secondColor);   // green

不定元素

在数组解构中,可以通过...语法将数组中的其余元素赋值给一个特定的变量, 不定元素必须为最后一个条目。

let colors = ['red', 'green', 'blur'];
let [firstColor, ...restColor] = colors;
console.log(firstColor);    // red
console.log(restColor);     // ['green', 'blur']

我们可以通过不定元素的语法来实现数组的克隆

let colors = ['red', 'green', 'blur'];
let [ ...cloneColor] = colors;
console.log(cloneColor);    // ['red', 'green', 'blur']

混合解构

可以混合使用对象解构和数组解构来创建更多复杂的表达式,来从任何混杂着对象和数组的数据解构中提取信息

let node = {
    type: 'id',
    name: 'foo',
    loc: {
        start:{
            line: 1,
            column: 1
        },
        end: {
            line: 2,
            column: 2
        }
    },
    range: [0, 1, 2, 3]
}

let {loc: {start}, range: [startIndex]} = node;
console.log(start.line);    // 1
console.log(start.column);  // 1
console.log(startIndex);    // 0

解构参数

解构可以用在函数参数的传递过程中,当定义一个接受大量可选参数的Javascript函数时,我们通常会创建一个可选对象,将额外的参数定义为这个对象的属性。

// options的属性表示其他参数
function setCookie(name, vallue, options) {
    options = options || {};
    let secure = options.secure,
    path = options.path,
    domain = options.domain,
    expires = options.expires;
    
    // 业务代码
}

setCookie('type', 'js', {
    secure: true,
    expires: 6000
});

缺点:仅查看函数的声明部分,无法辨识函数的预期参数,必须通过阅读函数体才可以确定所有参数的情况。
改进:将options定义为解构参数,则可以更清晰地了解函数预期传入的参数。解构参数需要使用对象或数组解构模式代替命名参数

function setCookie(name, vallue, {secure, path, domain, expires}) {
    
    // 业务代码
}

setCookie('type', 'js', {
    secure: true,
    expires: 6000
});

必须传值的解构参数

解构参数有一个奇怪的地方,默认情况下,如果调用函数时不提供被解构的参数会导致程序抛出错误。

// 程序报错
setCookie('type', 'js')

缺失的第三个参数,其值为undefined。Javascript引擎实际上做了这些事↓

function setCookie(name, vallue, options) {
    
   let {secure, path, domain, expires} = options;
    // 业务代码
}

如果希望将解构参数定义为可选的,就必须为其提供默认值。

function setCookie(name, vallue, {secure, path, domain, expires} = {}) {
    
    // 业务代码
}

解构参数的默认值

可以为结构参数指定默认值。

function setCookie(name, vallue, {
    secure = false,
    path = '/',
    domain = 'example.com',
    expires = 6000}) {
    
    // 业务代码
}

缺点:函数声明比以前复杂,其次如果解构参数是可选的,任然需要添加一个空对象作为参数。
建议:对于对象类型的解构参数,为其赋予相同解构的默认参数↓

function setCookie(name, vallue, {
    secure = false,
    path = '/',
    domain = 'example.com',
    expires = 6000} = {
    secure = false,
    path = '/',
    domain = 'example.com',
    expires = 6000}) {
    
    // 业务代码
}

将默认值提取到一个独立对象中,并且使用该对象作为结构和默认参数的一部分,可以消除代码冗余。

const setCoolieDefault = {
    secure = false,
    path = '/',
    domain = 'example.com',
    expires = 6000
}
function setCookie(name, vallue, {
    secure = setCoolieDefault.secure,
    path = setCoolieDefault.path,
    domain = setCoolieDefault.domain,
    expires = setCoolieDefault.expires
    } = setCoolieDefault) {
    
    // 业务代码
}

优点:如果要改变默认值,只需要在setCoolieDefault中修改,修改的数据将自动同步到所有出现过的地方。