ES6学习笔记:解构

对象解构

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

我们想从这个对象中提取数据,需要一项项地去解析:

let type = node.type;
let name = node.name;

但是使用解构就可以使用更简单的语法提取数据:

let {type,name} = node;

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

对象解构语法在赋值语句的左侧使用了对象字面量,在此代码中, node.type 的值被存储到 type 本地变量中, node.name 的值则存储到 name 变量中。

此语法与对象字面量中的属性简写相同,type 与 name 标识符既声明了本地变量,也读取了对象的相应属性值。

注意!当使用解构来声明变量的时候,等号右侧必须有值,不然会抛出错误:

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

解构赋值

上面的示例是用于变量声明的,也可以在赋值的时候使用解构:

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

//两个本地变量
type = "Literal",
name = 5;

// 使用解构来赋值
({type,name} = node);

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

我们通过读取 node 对象来更改这type、name这两个本地变量的值。

注意!必须使用圆括号包裹结构赋值语句,因为暴露的花括号会被解析为代码块,而块语句不允许在赋值操作符(即等号)左侧出现字面量。
圆括号标示了里面的花括号并不是块语句、而应该被解释为表达式,从而允许完成赋值操作。

解构表达式的值为表达式右侧的值,也就是说({type,name} = node);的值是node。我们可以任意使用这个值,例如传递给函数:

function outputInfo(value) {
    console.log(value);       
}
outputInfo({ type, name } = node);

这里node就被传给了函数。

当解构赋值表达式的右侧( = 后面的表达式)的计算结果为 null 或 undefined 时,会抛出错误。

默认值

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

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

let { type, name, value } = node;

console.log(type);      // "Identifier"
console.log(name);      // "foo"
console.log(value);     // undefined

由于node对象中没有value属性,因此value被赋值为undefined。

你可以选择定义一个默认值,以便指定属性不存在时使用默认值。

let {type, name, value='abc'} = node;
console.log(value);     // abc

只有在node的对应属性缺失、或者对应的属性值为undefined的情况下,默认值才会被使用。

赋值给不同的本地变量名

上面的例子中都使用了对象中的属性名作为本地变量的名称。但如果想赋值给不同名的本地变量呢?
ES6允许你在给本地变量赋值时使用一个不同的名称:

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

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

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

type: localType这种语法表示要读取名为type的属性,并赋值给变量localType。
(这种方式有点别扭啊)

也可以给变量别名添加默认值:

let node = {
        type: "Identifier"
};

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

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

此处变量localName有一个默认值,由于node.name并不存在,因此其最终被赋予了默认值。

嵌套的对象解构

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属性。

每当有一个冒号在解构模式中出现,就意味着冒号之前的标识符代表需要检查的位置。

在刚刚为不同的本地变量名赋值中也是,type: localType就表示要在type中寻找。此处则是要在loc中寻找。

当冒号右侧存在花括号时,表示目标被嵌套在对象的更深一层中。

此时就代表我们要在loc内部寻找start。

也可以在嵌套解构中为本地变量使用不同的名称:

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

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

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

数组解构

数组解构的语法看起来与对象解构非常相似,只是将对象字面量替换成了数组字面量,解构作用在数组内部的位置上,而不是作用在对象的具名属性上。

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

let [firstColor, secondColor] = colors;

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

此处数组解构从 colors 数组中取出了 “red” 与 “green” ,并将它们赋值给 fristColor 与 secondColor 变量。

这些值是根据数据中的位置取出的,firstColor对应第一个值,secondColor对应第二个值,而变量名称和取值是无关的。(与对象解构不同)

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

你也可以在解构模式中忽略一些项,只给想提取的项提供变量名。
例如,只想取得数组中的第三个元素,那么不必给前两项提供变量名:

let [ , , thirdColor] = colors;

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

模式中 thirdColor 之前的逗号,是为数组前面的项提供的占位符。使用这种方法,你就可以轻易从数组任意位置取出值,而无须给其他项提供变量名。

解构赋值

同样,不光是可以声明变量,也可以为现有变量赋值:

let colors = [ "red", "green", "blue" ],
firstColor = "black",
secondColor = "purple";

[ firstColor, secondColor ] = colors;

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

数组解构有一个非常独特的用例,就是可以交换两个变量的值
一般的做法是引入第三个变量作为临时变量,但是使用数组解构就不需要:

let a = 1,
    b = 2;

[a,b] = [b,a]

console.log(a);     // 2
console.log(b);     // 1

左侧正如其他数组解构的左侧,右侧则是一个数组,为了互换而临时创建的数组。b 与 a 的值分别被复制到临时数组的第一个与第二个位置,并对该数组进行解构,结果两个变量就互换了它们的值。

与对象解构赋值相同,若等号右侧的计算结果为 null 或 undefined ,那么数组解构赋值表达式也会抛出错误。

默认值

数组解构赋值同样允许在数组任意位置指定默认值。当指定位置的项不存在、或其值为 undefined ,那么该默认值就会被使用。

let colors = [ "red" ];

let [firstColor, secondColor="green"] = colors;

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

此代码的 colors 数组只有一个项,因此没有能与 secondColor 匹配的项,又由于此处有个默认值, secondColor 的值就被设置为 “green” ,而不是 undefined 。

嵌套解构

与解构嵌套的对象相似,可以用类似的方式来解构嵌套的数组。在整个解构模式中插入另一个数组模式,解构操作就会下行到嵌套的数组中:

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

let [firstColor,[secondColor]] = colors;

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

secondColor在第二个方括号中,因此就要到第二层数组中取值。
与对象解构相似,你也能使用任意深度的数组嵌套。

剩余项

ES6的函数存在剩余参数的概念,指的是 …args可以将剩下的未明确声明的参数囊括。
而数组解构也有剩余项的概念,它使用 … 语法来将剩余的项目赋值给一个指定的变量。

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"

第一个项赋给了firstColor,剩余的项则赋给了一个新的restColors数组。
若要取出特定项并要求保留剩余的值,则剩余项是非常有用的。

剩余项还可以用于克隆数组
在ES5中一般使用concat来克隆数组:

var colors = [ "red", "green", "blue" ];
var clonedColors = colors.concat();

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

不给concat传任何参数,就会获得原数组的一个克隆。

而现在可以使用剩余项的语法达到相同的目的:

var [...clonedColors] = colors;

和数组解构语法相同,使用剩余项使得colors的所有值都赋给了clonedColors数组。

剩余项必须是数组解构模式中最后的部分,之后不能再有逗号,否则就是语法错误。

混合解构

对象与数组解构能被用在一起,以创建更复杂的解构表达式。在对象与数组混合而成的结构中,这么做便能准确提取其中你想要的信息片段。

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

此处loc与range只是对应于node对象中属性的位置。

混合使用对象与数组解构, node 的任何部分都能提取出来。对于从 JOSN 配置结构中抽取数据来说,这种方法尤其有用,因为它不需要探索整个结构。

参数解构

在传递函数参数时,当函数接受大量可选参数时,一个常用模式是创建一个 options 对象,其中包含了附加的参数:

function setCookie(name, value, options) {

    options = options || {};

    let secure = options.secure,
        path = options.path,
        domain = options.domain,
        expires = options.expires;

}

此处,name与value属性是必须的,而其他参数则不是,通过一个options选项统一传入。

参数解构提供了更清楚地标明函数期望输入的替代方案。它使用对象或数组解构的模式替代了具名参数:

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

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

第三个参数使用了解构来解析必要的数据。此时可选项目存在于额外的参数组中,若传递了则可以直接获取值,若没有传递则为undefined。

但是这种方式要求参数解构一定有值,因为js引擎实际上是这样做的:

function setCookie(name, value, options) {

    let { secure, path, domain, expires } = options;


}

因此如果options没有传,那么就相当于解构赋值的右侧为null,那么肯定会报错:

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

因此一个比较好的实践是给解构的参数提供默认值:

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

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

参数解构的默认值

你可以为参数解构提供可解构的默认值,就像在解构赋值时所做的那样,只需在其中每个参数后面添加等号并指定默认值即可。例如:

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

    // ...
}

此代码中参数解构给每个属性都提供了默认值,所以你可以避免检查指定属性是否已被传入(以便在未传入时使用正确的值)。而整个解构的参数同样有一个默认值,即一个空对象,令该参数成为可选参数。这么做使得函数声明看起来比平时要复杂一些,但却是为了确保每个参数都有可用的值而付出的微小代价。

总结

解构使得在 JS 中操作对象与数组变得更容易。使用熟悉的对象字面量与数组字面量语法,可以将数据结构分离并只获取你感兴趣的信息。对象解构模式允许你从对象中进行提取,而数组模式则能用于数组。

对象与数组解构都能在属性或项未定义时为其提供默认值

在赋值表达式右侧的值为 null 或 undefined 时,两种模式都会抛出错误。

你也可以在深层嵌套的数据结构中使用对象与数组解构,下行到该结构的任意深度。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值