ES6面试点-解构赋值

1,695 阅读7分钟

解构赋值

通过解构赋值, 可以将属性/值从对象/数组中取出,赋值给其他变量。简单理解就是等号的左边和右边相等。主要分为对象的解构和数组的解构,在没有解构赋值的时候,我们赋值是这样的

let arr = [0,1,2]
let a = arr[0]
let b = arr[1]
let c = arr[2]

数组的解构赋值

数组解构的基本用法

let [a, b, c] = [1, 2, 3] // a=1, b=2, c=3
let [d, [e], f] = [1, [2], 3] // 嵌套数组解构 d=1, e=2, f=3
let [g, ...h] = [1, 2, 3] // 数组拆分 g=1, h=[2, 3]
let [i,,j] = [1, 2, 3] // 不连续解构 i=1, j=3
let [k,l] = [1, 2, 3] // 不完全解构 k=1, l=2

变量声明并赋值时的解构

let arr = [0, 1, 2]
let [a, b, c] = arr
console.log(a) // 0
console.log(b) // 1
console.log(c) // 2

变量先声明后赋值时的解

通过解构分离变量的声明,可以为一个变量赋值

var a, b;

[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2
为了防止从数组中取出一个值为undefined的对象,可以在表达式左边的数组中为任意对象预设默认值。
let arr = [, 1, 2] // 等价于 let arr1 = [undefined, 1, 2]
let [a = '我是默认值', b, c] = arr
console.log(a) // '我是默认值'
console.log(b) // 1
console.log(c) // 2
在解构赋值的过程中,a=undefined时,会使用默认值
那么当a=null时呢?当a=null时,那么a就不会使用默认值,而是使用null
let arr1 = [null, 1, 2]
let [a = '我是默认值', b, c] = arr1
console.log(a) // null
console.log(b) // 1
console.log(c) // 2

解析一个从函数返回的数组

从一个函数返回一个数组是十分常见的情况。解构使得处理返回值为数组时更加方便。在下面例子中,要让 [1, 2] 成为函数的 f() 的输出值,可以使用解构在一行内完成解析。

function f() {
  return [1, 2];
}

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

===============

// 忽略某些返回值
function f() {
  return [1, 2, 3];
}

var [a, , b] = f();
console.log(a); // 1
console.log(b); // 3

===============

// 忽略全部返回值:
[,,] = f();

将剩余数组赋值给一个变量

如果剩余元素右侧有逗号,会抛出 SyntaxError,因为剩余元素必须是数组的最后一个元素。

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

============

var [a, ...b,] = [1, 2, 3];
// SyntaxError: rest element may not have a trailing comma

数组的拼接

let a = [0, 1, 2]
let b = [3, 4, 5]
let c = a.concat(b)
console.log(c) // [0, 1, 2, 3, 4, 5]

let d = [...a, ...b]
console.log(d) // [0, 1, 2, 3, 4, 5]

数组的克隆

// 数组的克隆
// 假如我们简单地把一个数组赋值给另外一个变量
let a = [0,1,2,3]
let b = a
b.push(4)
console.log(a) // [0,1,2,3,4]
console.log(b) // [0,1,2,3,4]
// 这只是简单的把引用地址赋值给b,而不是重新开辟一个内存地址
// 所以a和b共享了同一个内存地址,
// 该内存地址的更改,会影响到所有引用该地址的变量
用下面的方法,把数组进行克隆一份,互不影响
let a = [0,1,2,3]
let b = [...a]
b.push(4)
console.log(a) // [0,1,2,3]
console.log(b) // [0,1,2,3,4]

对象的解构赋值

对象解构的基本用法:

let {a, b} = {a: 'aaaa', b: 'bbbb'} // a='aaaa' b='bbbb'
let obj = {d: 'aaaa', e: {f: 'bbbb'}}
let {d, e:{f}} = obj // 嵌套解构 d='aaaa' f='bbbb'
let g;
(g = {g: 'aaaa'}) // 以声明变量解构 g='aaaa'
let [h, i, j, k] = 'nice' // 字符串解构 h='n' i='i' j='c' k='e'
let {length} = "hello swr"
console.log(length) // 9

基本赋值

var o = {p: 42, q: true};
var {p, q} = o;

console.log(p); // 42
console.log(q); // true

无声明赋值

一个变量可以独立于其声明进行解构赋值。

var a, b;
({a, b} = {a: 1, b: 2});
注意:赋值语句周围的圆括号 ( ... ) 在使用对象字面量无声明解构赋值时是必须的。{a, b} = {a: 1, b: 2} 不是有效的独立语法,因为左边的 {a, b} 被认为是一个块而不是对象字面量。然而,({a, b} = {a: 1, b: 2}) 是有效的,正如 var {a, b} = {a: 1, b: 2}你的 ( ... ) 表达式之前需要有一个分号,否则它可能会被当成上一行中的函数执行。

给新的变量名赋值

可以从一个对象中提取变量并赋值给和对象属性名不同的新的变量名。

var o = {p: 42, q: true};
var {p: foo, q: bar} = o;
 
console.log(foo); // 42 
console.log(bar); // true 

默认值

变量可以先赋予默认值。当要提取的对象没有对应的属性,变量就被赋予默认值。

var {a = 10, b = 5} = {a: 3};

console.log(a); // 3
console.log(b); // 5

使用场景

变量赋值

先来看我们在平时开发中是怎么使用es5对变量赋值的:

var data = {userName: 'aaaa', password: 123456}
var userName = data.userName
var password = data.password
console.log(userName)
console.log(password)
var data1 = ['aaaa', 123456]
var userName1 = data1[0]
var password1 = data1[1]
console.log(userName1)
console.log(password1)
上面两个例子是最简单的例子,用传统es5变量赋值,然后调用,这么写的问题就是显得代码啰嗦,明明一行可以搞定的事情非要用三行代码,来看看解构赋值是怎么干的:
const {userName, password} = {userName: 'aaaa', password: 123456}
console.log(userName)
console.log(password)
const [userName1, password1] = ['aaaa', 123456]
console.log(userName1)
console.log(password1)

函数参数的定义

一般我们在定义函数的时候,如果函数有多个参数时,在es5语法中函数调用时参数必须一一对应,否则就会出现赋值错误的情况,来看一个例子:

function personInfo(name, age, address, gender) {
  console.log(name, age, address, gender)
}
personInfo('william', 18, 'changsha', 'man')
上面这个例子在对用户信息的时候需要传递四个参数,且需要一一对应,这样就会极易出现参数顺序传错的情况,从而导致bug,接下来来看es6解构赋值是怎么解决这个问题的:
function personInfo({name, age, address, gender}) {
  console.log(name, age, address, gender)
}
personInfo({gender: 'man', address: 'changsha', name: 'william', age: 18})
这么写我们只需要知道要传什么参数就行来,不需要知道参数的顺序也没问题

交换变量的值

在es5中我们需要交换两个变量的值需要借助临时变量的帮助,来看一个例子:

var a=1, b=2, c
c = a
a = b
b = c
console.log(a, b)
来看es6怎么实现:
let a=1, b=2;
[b, a] = [a, b]
console.log(a, b)

函数的默认参数

在日常开发中,经常会有这种情况:函数的参数需要默认值,如果没有默认值在使用的时候就会报错,来看es5中是怎么做的:

function saveInfo(name, age, address, gender) {
  name = name || 'william'
  age = age || 18
  address = address || 'changsha'
  gender = gender || 'man'
  console.log(name, age, address, gender)
}
saveInfo()
es6中的使用的方法:
function saveInfo({name= 'william', age= 18, address= 'changsha', gender= 'man'} = {}) {
  console.log(name, age, address, gender)
}
saveInfo()
在函数定义的时候就定义了默认参数,这样就免了后面给参数赋值默认值的过程,是不是看起来简单多了

解构嵌套对象和数组

const metadata = {
  title: 'Scratchpad',
  translations: [
    {
      locale: 'de',
      localization_tags: [],
      last_edit: '2014-04-14T08:43:37',
      url: '/de/docs/Tools/Scratchpad',
      title: 'JavaScript-Umgebung'
    }
  ],
  url: '/en-US/docs/Tools/Scratchpad'
};

let {
  title: englishTitle, // rename
  translations: [
    {
       title: localeTitle, // rename
    },
  ],
} = metadata;

console.log(englishTitle); // "Scratchpad"
console.log(localeTitle);  // "JavaScript-Umgebung"

For of 迭代和解构

var people = [
  {
    name: 'Mike Smith',
    family: {
      mother: 'Jane Smith',
      father: 'Harry Smith',
      sister: 'Samantha Smith'
    },
    age: 35
  },
  {
    name: 'Tom Jones',
    family: {
      mother: 'Norah Jones',
      father: 'Richard Jones',
      brother: 'Howard Jones'
    },
    age: 25
  }
];

for (var {name: n, family: {father: f}} of people) {
  console.log('Name: ' + n + ', Father: ' + f);
}

// "Name: Mike Smith, Father: Harry Smith"
// "Name: Tom Jones, Father: Richard Jones"

从作为函数实参的对象中提取数据

function userId({id}) {
  return id;
}

function whois({displayName: displayName, fullName: {firstName: name}}){
  console.log(displayName + " is " + name);
}

var user = { 
  id: 42, 
  displayName: "jdoe",
  fullName: { 
      firstName: "John",
      lastName: "Doe"
  }
};

console.log("userId: " + userId(user)); // "userId: 42"
whois(user); // "jdoe is John"

对象属性计算名和解构

let key = "z";
let { [key]: foo } = { z: "bar" };

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

解构对象时会查找原型链(如果属性不在对象自身,将从原型链中查找)

// 声明对象 和 自身 self 属性
var obj = {self: '123'};
// 在原型链中定义一个属性 prot
obj.__proto__.prot = '456';
// test
const {self, prot} = obj;
// self "123"
// prot "456"(访问到了原型链)