2

一、概念

对象是属性的集合,从对象里取值,ES3/5只能逐个取,而解构赋值表达式可以实现批量取值,赋值。

// 数据对象person 
var person = {
    name: 'john',
    age: 22
}

// ES5的方式:
var name = person.name,
    age = person.age;
    
// ES6解构赋值方式
var {name, age} = person;

二、对象解构赋值

2.1 语法

// 格式1:声明即赋值
var {sourceObjKey1: targetValName1=defaultVal1, sourceObjKey2: targetValName2=defaultVal2, ..., sourceObjKeyN: targetValNameN=defaultValN} = sourceObj;

// 格式2, 先声明,后赋值。此时必须使用括号包裹整个赋值表达式
var targetValName1,targetValName2, ..., targetValNameN;
({sourceObjKey1: targetValName1=defaultVal1, sourceObjKey2: targetValName2 = defaultVal2, ...,sourceObjKeyN: targetValNameN=defaultValN} = sourceObj);
  • sourceObj:表示数据源对象
  • sourceObjKey:表示需要从数据源对象中取值的属性名称,如果和valName1同名,可以省略不写;
  • targetValName:表示需要把获取的属性值赋值给的模板变量,即targetValName=obj[sourceObjKey]
  • defaultVal:表示sourceObj对象不包含属性sourceObjKey(或者取值为undefined)时指定的默认值,可选的。

所以用ES5的表示方式相当于:

var targetValName1 = sourceObj[sourceObjKey1],
    targetValName2 = sourceObj[sourceObjKey2],
    targetValNameN = sourceObj[sourceObjKeyN];

如果key和valName的相同,可以简写为:

// 格式1:声明即赋值
var {targetValName1=defaultVal1, targetValName2=defaultVal2, ..., targetValNameN=defaultValN} = sourceObj;

// 格式2, 先声明,后赋值。此时括号是必须的
var targetValName1,targetValName2, ..., targetValNameN;
({targetValName1=defaultVal1, targetValName2=defaultVal2, ..., targetValNameN=defaultValN} = sourceObj);

大部分场景都是使用简写的方式。

假设有个对象dad:

// Object
var dad = {
    name: 'john',
    age: 30,
    children: [
        {
            name: 'Tom',
            age: 2
        },
        {
            name: 'Jim',
            age: 1
        }
    ]
}

现在要把dad属性分别保存到变量name, age, children里:

// 格式1: 声明即赋值
var {name:name, age:age, children:children} = dad;
console.log(name); // john
console.log(age); // 30
console.log(children.length); // 2

// 格式2, 先声明,后赋值。此时括号是必须的,否则报语法错误
var name, age, children;
({name:name, age:age, children:children} = dad)

对应的简写格式:

// 格式1
var {name, age, children} = dad;
// 格式2
var name, age, children;
({name, age, children} = dad)

2.2 默认值

对于没有匹配到 或者匹配到的属性值为undefined的变量可以指定默认值。

dad.name = void 0; // dad定义见上例,
var { weight = '66kg', name='new name'} = dad; // dad没有定义weight属性
console.log(weight, name); // weight='66kg', name='new name'

2.3 动态属性名称

在上面的语法结构中

var {key1: valName1, key2: valName2, ..., keyN: valNameN} = obj;

其中key1,key2...keyN还可以是个变量,为了标识他们是变量而不是字面量,则需用用中括号包裹:

var key = 'name';
var {[key]:val } = dad;
console.log(val); // 'john'
key = 'age';
({[key]:val } = dad);
console.log(val); // 30

2.4 小结

  1. 解构赋值的前提是同构,即左右两边的对象解构要一致;
  2. 对象解构赋值在绑定值时根据属性key(key的名称和结构位置)进行匹配的。赋值操作符(等号)两边的解构要一致才能正确赋值;
  3. 解构赋值表达式是赋值表达式的语法糖。

三、数组解构赋值

数组的属性名称叫索引,表示的是位置。所以跟对象的解构赋值相比语法上有些不同:

3.1 语法

// 语法格式1: 声明即赋值
var arr = [1, 2, 3, 4, 5, 6];
var [f, s, t] = arr;
// 语法格式2: 先声明,后赋值
var a, b, c;
[a, b, c] = arr;

console.log([f, s, t]) // [1, 2, 3]
console.log([a, b, c]) // [1, 2, 3]

数组解构赋值在绑定值时根据属性下标和进行匹配的。赋值操作符(等号)两边的解构要一致才能正确赋值,如果匹配不成功则取值为undefined。

3.2 跳跃式的赋值

var arr = [1, 2, 3];
var [,f,s] = arr; // f=2, s=3

3.3 获取数组剩下的元素

通过rest操作符(...)修饰变量可以实现获取数组剩下的元素:

var arr = [3, 4, 5, 6];
var [a, ...b] = arr;
console.log(b); // [4,5,6]

变量b获取数组arr剩余的元素,此时注意变量b必须放在最后,否则语法错误。如果没有剩余的元素,则为空数组,

var arr = [3];
var [a, b, ...c] = arr;
console.log(a) // 3
console.log(b) // undefined
console.log(c) // []

3.4 嵌套

// 获取二维数组的元素
var [[a00=1], [,a11]] = [[1, 2], [3, 4]];
console.log(`a00=${a00}, a11=${a11}`)

// 继续上面的例子,现在的需求是把dad对象里第2个孩子的属性保存到name,age变量里(这个写法就复杂了,结合了数组解构赋值和对象解构赋值):
var {children: [,{name, age}]} = dad;
console.log([name, age])

注意: 复杂的嵌套降低了可读性,需要权衡下是否要使用

3.5 数组也是对象

数组本身也是对象,数组的赋值解构也可以通过对象解构赋值的方式表示:

var arr = [1, 2, 3];
var {0:f, 1:s, 2:t} = arr; // 等价 var [f, s, t] = arr;
console.log([f, s, t]) // f=1, s=2, t=3

这样写确实麻烦哈,数组解构赋值是对数组对象解构赋值的的简化。他们的赋值匹配模式本质上是一样的

四、对非对象的变量进行解构

var {a } = 1, // undefined
    {b} = true, // undefined
    {c} = 'a', // undefined
    {d} = null, // 类型错误
    {e} = undefined; // 类型错误

JS对这些基本类型数字,字符串,boolean都定义了对应的对象类型,在对这些类型的数据进行解构赋值操作时,会转成对应的对象类型。但是null, undefined却没有对应的对象类型,所有在运行时会报类型错误。

五、一些应用场景

总结一句话: 只要是发生赋值操作的场景都可以使用解构赋值表达式。

5.1 函数参数解构赋值

function log({title: tag = "Debug", message}) {
    console.log(`${tag}: ${message}`);
}

log({title: 'Info', message: 'This is a log message'})

要使用ES5实现上面的功能,大致如:

function log(option) {
    if(!option) option= {};
    var tag = option.title || 'Debug',
        message = option.message;
    console.log(`${tag}: ${message}`);
}
log({title: 'Info', message: 'This is a log message'})

解构赋值用作函数的形参使得参数更加直观明了,并且可以赋值默认值。ES5的写法得读代码才能看到具体需要什么参数。

5.2 for-of语句

可以对集合元素进行解构操作,更方便循环体内取值。

for(var {name, age} of dad.children) {
    console.log(`name: ${name}, age: ${age}`)
}

5.3 函数返回值解构

// 方便正则取值
var url = 'https://developer.mozilla.org/en-US/Web/JavaScript';
var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url);
console.log(parsedURL); 
var [, protocol, fullhost, fullpath] = parsedURL;
console.log(protocol); // "https"

参考

  1. MDN Destructuring assignment
  2. ES6 In Depth: Destructuring

普拉斯强
2.7k 声望53 粉丝

Coder


下一篇 »
JS-Array