春风得意马蹄疾
一日看尽长安花

深入理解ES6解构赋值:三、混合解构和其他解构

上一篇:深入理解ES6解构赋值:二、数组解构

【混合解构】

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

let node = {
    type: "Identifier",
    name: "foo",
    loc: {
        start: {
            line: 1,
            column: 1
        },
        end: {
            line: 1,
            column: 4
        }
    },
    range: [0, 3]
};
let {
    loc: { start },
    range: [ startIndex ]
} = node;
console.log(start.line); // 1
console.log(start.column); // 1
console.log(startIndex); // 0

这段代码分别将node.loc.start和node.range[0]提取到变量start和startlndex中

解构模式中的loc和range仅代表它们在node对象中所处的位置(也就是该对象的属性)。当使用混合解构的语法时,则可以从node提取任意想要的信息。这种方法极为有效,尤其是从JSON配置中提取信息时,不再需要遍历整个结构了

【解构参数】

解构可以用在函数参数的传递过程中,这种使用方式更特别。当定义一个接受大量可选参数的JS函数时,通常会创建一个可选对象,将额外的参数定义为这个对象的属性

// options 上的属性表示附加参数
function setCookie(name, value, options) {
    options = options || {};
    let secure = options.secure,
        path = options.path,
        domain = options.domain,
        expires = options.expires;
        // 设置 cookie 的代码
}
// 第三个参数映射到 options
setCookie("type", "js", {
    secure: true,
    expires: 60000
});

许多JS库中都有类似的setCookie()函数,而在示例函数中,name和value是必需参数,而secure、path、domain和expires则不然,这些参数相对而言没有优先级顺序,将它们列为额外的命名参数也不合适,此时为options对象设置同名的命名属性是一个很好的选择。现在的问题是,仅查看函数的声明部分,无法辨识函数的预期参数,必须通过阅读函数体才可以确定所有参数的情况

如果将options定义为解构参数,则可以更清晰地了解函数预期传入的参数。解构参数需要使用对象或数组解构模式代替命名参数

function setCookie(name, value, { secure, path, domain, expires }) {
// 设置 cookie 的代码
}
setCookie("type", "js", {
    secure: true,
    expires: 60000
});

这个函数与之前示例中的函数具有相似的特性,只是现在使用解构语法代替了第3个参数来提取必要的信息,其他参数保持不变,但是对于调用setCookie()函数的使用者而言,解构参数变得更清晰了

【必须传值的解构参数】

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

// 出错!
setCookie("type", "js");

缺失的第3个参数,其值为undefined,而解构参数只是将解构声明应用在函数参数的一个简写方法,其会导致程序抛出错误。当调用setCookie()函数时,JS引擎实际上做了以下这些事情

function setCookie(name, value, options) {
    let { secure, path, domain, expires } = options;
    // 设置 cookie 的代码
}

如果解构赋值表达式的右值为null或undefined,则程序会报错。同理,若调用setCookie()函数时不传入第3个参数,也会导致程序抛出错误

如果解构参数是必需的,大可忽略掉这些问题;但如果希望将解构参数定义为可选的,那么就必须为其提供默认值来解决这个问题

function setCookie(name, value, { secure, path, domain, expires } = {}) {
    // ...
}

这个示例中为解构参数添加了一个新对象作为默认值,secure、path、domain及expires这些变量的值全部为undefined,这样即使在调用setCookie()时未传递第3个参数,程序也不会报错

【默认值】

可以为解构参数指定默认值,就像在解构赋值语句中那样,只需在参数后添加等号并且指定一个默认值即可

function setCookie(name, value,
    {
        secure = false,
        path = "/",
        domain = "example.com",
        expires = new Date(Date.now() + 360000000)
    } = {}
) {
    // ...
}

在这段代码中,解构参数的每一个属性都有默认值,从而无须再逐一检查每一个属性是否都有默认值。然而,这种方法也有很多缺点。首先,函数声明变得比以前复杂了;其次,如果解构参数是可选的,那么仍然要给它添加一个空对象作为参数,否则像setCookie("type","js")这样的调用会导致程序抛出错误

  对于对象类型的解构参数,最好为其赋予相同解构的默认参数

function setCookie(name, value,
    {
        secure = false,
        path = "/",
        domain = "example.com",
        expires = new Date(Date.now() + 360000000)
    } = {
        secure : false,
        path : "/",
        domain : "example.com",
        expires : new Date(Date.now() + 360000000)        
    }
) {
    // ...
}

现在函数变得更加完整了,第一个对象字面量是解构参数,第二个为默认值。但是这会造成非常多的代码冗余,可以将默认值提取到一个独立对象中,并且使用该对象作为解构和默认参数的一部分,从而消除这些冗余

const setCookieDefaults = {
    secure : false,
    path : "/",
    domain : "example.com",
    expires : new Date(Date.now() + 360000000)    
}
function setCookie(name, value,{
        secure = setCookieDefaults.secure,
        path = setCookieDefaults.path,
        domain = setCookieDefaults.domain,
        expires = setCookieDefaults.expires        
    }=setCookieDefaults) {
    // ...
}

在这段代码中,默认值已经被放到setCookieDefaults对象中,除了作为默认参数值外,在解构参数中可以直接使用这个对象来为每一个绑定设置默认参数。使用解构参数后,不得不面对处理默认参数的复杂逻辑,但它也有好的一面,如果要改变默认值,可以立即在setCookieDefaults中修改,改变的数据将自动同步到所有出现过的地方

【字符串解构】

字符串也可以解构赋值。这是因为,字符串被转换成了一个类似数组的对象

const [a, b, c, d, e] = 'hello';
console.log(a);//"h"
console.log(b);//"e"
console.log(c);//"l"
console.log(d);//"l"
console.log(e);//"o"

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值

const {length} = 'hello';
console.log(length);//5

【数值和布尔值解构】

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象

let {toString:s1} = 123;
console.log(s1 === Number.prototype.toString);//true
let {toString:s2} = true;
console.log(s2 === Boolean.prototype.toString);//true

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错

let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
Like
Like Love Haha Wow Sad Angry
赞(1) 打赏
未经允许不得转载:栗子纪blog » 深入理解ES6解构赋值:三、混合解构和其他解构
分享到: 更多 (0)
0 0 vote
Article Rating
Subscribe
提醒
guest
0 评论
Inline Feedbacks
View all comments

创作不易,打赏一下作者买瓶洗发露

支付宝扫一扫打赏

微信扫一扫打赏

0
Would love your thoughts, please comment.x
()
x