# (ES6版本新特性):你想要的解构知识!

115 阅读8分钟

1. 对象解构

1.1 解构赋值

let node = {
    type: "Identifier",
    name: "foo"
};

let { type, name } = node; // 解构语句

console.log(type);    // "Identifier"

console.log(name);    // "foo"

node对象里面的属性解析出来,赋值给本地构造的两个变量。

注意点:

  1. 对一个对象使用解构语句的时候,必须加上{}包裹创建的变量名,因为你要做的是从一个对象中去拿变量,得先进去才可以拿(通俗地解释)。

  2. 解构的变量名,必须在被解析的对象node中存在(相当于想点名回答问题得知道一个人的名字)。否则创建的变量名会返回undefind

  3. 不必将数据全部解构出来,可以只解构出你想要的变量。常用于前端业务中的数据过滤(或称为机器学习中的数据清洗)。

  4. 当你使用解构赋值语句时,如果所指定的本地变量在对象中没有找到同名属性,那么该变量会被赋值为 undefined。

1.2 自定义解构变量名(为解构出的变量起别名)

let node = {
    type: "Identifier",
    name: "foo"
};

// 将名称type换成了localType,值依旧是解构出来的值
let { type: localType, name: localName } = node;

console.log(localType); // "Identifier"

console.log(localName); // "foo"

此代码使用了解构赋值来声明 localTypelocalName 变量,分别获得了 node.typenode.name 属性的值。type: localType 这种语法表示要读取名为 type 的属性,并把它的值存储在变量 localType 上。该语法实际上与传统对象字面量语法相反,传统语法将名称放在冒号左边、值放在冒号右边;而在本例中,则是名称在右边,需要进行值读取的位置则被放在了左边。

也可以给变量别名添加默认值,依然是在本地变量名称后添加等号与默认值,例如:

let node = {
    type: "Identifier"
};

let { type: localType, name: localName = "bar" } = node;

console.log(localType); // "Identifier"

console.log(localName); // "bar"

1.3 嵌套的对象解构

let node = {
        type: "Identifier",
        name: "foo",
        loc: {
            start: {
                line: 1,
                column: 1
        },
        end: {
                line: 1,
                column: 4
        }
    }
};

let { loc: { start }} = node;

console.log(start.line); // 1

console.log(start.column); // 1

本例中的解构模式使用了花括号,表示应当下行到 node 对象的 loc 属性内部去寻找 start 属性。每当有一个冒号在解构模式中出现,就意味着冒号之前的标识符代表需要检查的位置,而冒号右侧则是赋值的目标。当冒号右侧存在花括号时,表示目标被嵌套在对象的更深一层中。

在对象的嵌套解构中同样能为本地变量使用不同的名称:

let node = {
        type: "Identifier",
        name: "foo",
        loc: {
            start: {
                line: 1,
                column: 1
        },
        end: {
                line: 1,
                column: 4
        }
    }
};

let { loc: { start: localStart }} = node;

console.log(localStart.line); // 1

console.log(localStart.column); // 1

node.loc.start 的值被存储在一个新的本地变量 localStart 上,解构模式可以被嵌套在任意深度的层级,并且在每个层级的功能都一样。

2. 数组解构

2.1 解构赋值

let colors = [ "red", "green", "blue" ];

let [ firstColor, secondColor ] = colors;

console.log(firstColor); // "red"

console.log(secondColor); // "green"

此处数组解构从 colors 数组中取出了 "red""green" ,并将它们赋值给 fristColor

secondColor 变量。这些值被选择,是由于它们在数组中的位置,实际的变量名称是任意

的。任何没有在解构模式中明确指定的项都会被忽略。数组本身并没有以任何方式被改变。

当然,也可以在解构模式中忽略一些项,并且只给感兴趣的项提供变量名。例如,若只想获取数组中的第三个元素,那么不必给前两项提供变量名。

let colors = [ "red", "green", "blue" ];

let [ , , thirdColor ] = colors;

console.log(thirdColor); // "blue"

注意点:

  1. 对一个数组使用解构语句的时候,必须加上[]包裹创建的变量名,因为你要做的是从一个数组中去拿值,得先进去才可以拿(通俗地解释)。

  2. 与对象解构不一样的是,数组解构不在乎创建的本地变量名称,而是在乎本地变量相对于被解构的数组 colors 中元素的顺序位置。

  3. 不必将数据全部解构出来,可以根据数组中对应的位置只解构出你想要的值。

  4. 当你使用解构赋值语句时,如果所当指定位置的项不存在、或其值为 undefined,那么该变量会被赋值为 undefined

2.2 数组解构的变量值互换(类似于 C++ 中 swap 函数)

// 在 ES6 中互换值

let a = 1,

b = 2;

[ a, b ] = [ b, a ];
// 效果等同于:
// let tmp = a;
// a = b;
// b = tmp;

console.log(a); // 2

console.log(b); // 1

这个小技巧类似于 C++ 中 swap 函数

2.3 嵌套数组的解构

let colors = [ "red", [ "green", "lightgreen" ], "blue" ];

let [ firstColor, [ secondColor ] ] = colors;

console.log(firstColor); // "red"

console.log(secondColor); // "green"

此处的 secondColor 变量指向了 colors 数组中的 "green" 值,该项被包含在第二个数组中,因此解构模式就要把 secondColor 包裹上方括号。与对象解构相似,你也能使用任意深度的数组嵌套。

2.4 数组剩余项的解构

数组解构有个类似的、名为剩余项( rest items )的概念,它使用 ... 语法来将剩余的项目赋值给一个指定的变量,示例如下:

let colors = [ "red", "green", "blue" ];

let [ firstColor, ...restColors ] = colors;

console.log(firstColor); // "red"

console.log(restColors.length); // 2

console.log(restColors[0]); // "green"

console.log(restColors[1]); // "blue"

colors 数组的第一项被赋值给了 firstColor 变量,而剩余的则赋值给了一个新的 restColors 数组; restColors 数组则有两个项:"green""blue" 。若要取出特定项并要求保留剩余的值,剩余项非常有用。

2.5 数组剩余项的另一个用途---类似concat()方法

// 在 ES5 中克隆数组

var colors = [ "red", "green", "blue" ];

var clonedColors = colors.concat();

console.log(clonedColors); //"[red,green,blue]"
// 在 ES6 中克隆数组

let colors = [ "red", "green", "blue" ];

let [ ...clonedColors ] = colors;

console.log(clonedColors); //"[red,green,blue]"

剩余项被用于将 colors 数组的值复制到 clonedColors 数组中。

3. 对象与数组的混合解构

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.startnode.range[0] 提取出来,并将它们的值分别存储到 startstartIndex 中。要记住解构模式中的 loc: range: 只是对应于 node 对象中属性的位置。混合使用对象与数组解构,node 的任何部分都能提取出来。

4. 参数解构(针对于函数的参数进行解构,抽取自己需要的数据)

4.1 函数参数的解构原理分析以及限制

未对options参数进行解构前的代码:

// 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
});

对options参数进行解构后的代码:

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

    // 设置 cookie 的代码
}

setCookie("type", "js", {
    secure: true,
    expires: 60000
});

对options参数进行解构的原理其实是这样的:

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

setCookie("type", "js", {
    secure: true,
    expires: 60000
});

参数解构虽好,但是在这里,解构参数成了一个必选参数,也就是我们调用 setCookie 方法的时候,必须要传入解构参数 options ,否则会报错,为什么呢?看下面这个例子。

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

setCookie('type', 'js'); // Uncaught TypeError: Cannot destructure property `secure` of 'undefined' or 'null'.

由于对 options 参数进行解构的原理其实就是在函数内部对这个参数进行解构,然而我们仔细想想,当调用 setCookie 方法的时候,不传 options 参数进去,实际上在函数内部就没有 options 这个参数变量(因为压根就没有options 参数传进来),也就是俗称的 options 为未定义的变量,既然变量没有定义,又怎么能够使用呢?

当然这个问题也有解决方法,就是为参数解构提供可解构的默认值。

4.2 参数解构的默认值:

解决解构参数成为调用方法的必选参数限制:

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

   console.log(name, value);

}

setCookie('type', 'js'); // type js

为第三个参数提供了一个空对象作为其默认值。给解构的参数提供默认值,也就意味着若未向 setCookie() 函数传递第三个参数,则 securepathdomainexpires 的值全都会是 undefined ,此时不会有错误被抛出。

为参数解构提供可解构的默认值:(只需在其中每个参数后面添加等号并指定默认值即可。)

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

setCookie("type", "js", {
    secure: true,
    expires: 60000
});
// 输出结果:true '/' 'example.com' 60000

在此代码中,参数解构给每个属性都提供了默认值,在未传入这些参数时,比如 pathdomain 都为设置解构参数时的默认值。而整个解构的参数同样有一个默认值,即一个空对象,令该参数成为可选参数。

这个方法即使得解构的参数成为可选项,而且还为每个属性在未传入函数时都提供了默认值,还挺不错!

参考文献:

《深入理解ES6》