【重学前端】ECMAScript6 - 变量的解构赋值

89 阅读3分钟

变量的解构赋值

数组的解构赋值

let [a, b] = [1, 2]; // a = 1, b = 2

let [a, [[b], c]] = [1, [[2], 3]]; // a = 1, b = 2

let [ , , c] = [1, 2, 3]; // c = 3

let [a, ...b] = [1, 2, 3]; // a = 1, b = [2, 3]

let [a, b, ...c] = [1]; // a = 1, b = undefined, c = []

let [a, [b], c] = [1, [2, 3], 4]; // a = 1, b = 2, c = 4

我们可以看到,数组的解构赋值关键在于相同格式下的位置匹配。而且有一点我们需要关注,这里的数组并不是严格意义上的数组,而是拥有可迭代结构的数据(此部分与Iterator相关)。比如我们的arguments对象,我们也经常在写形参时直接调用解构赋值。

原生具备Iterator接口的数据结构有:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

我们一个个来演示各自解构出来的分别是什么:

// Map解构:得到entry值
const map = new Map();
map.set(1, 'a');
map.set(2, 'b');

let [a, b] = map;
console.log(a); // [1, 'a']
console.log(b); // [2, 'b']
// Set解构:得到单个值
const set = new Set();
set.add(1);
set.add(2, 'b');

let [a, b] = set;
console.log(a); // 1
console.log(b); // 2
// String解构:得到单个字符
const str = '123';
const [a, b, c] = str;
console.log(a); // '1'
console.log(b); // '2'
// TypedArray解构
const typedArr = new Int8Array([1,2]);

let [a, b] = typedArr;
console.log(a); // 1
console.log(b); // 2
// arguments解构
function func(a, ...b) {
  console.log(a); // 1
  console.log(b); // [2, 3]
}

func(1,2,3);
<body>
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <script>
      // NodeList解构
      const divs = document.getElementsByTagName('div');
      const [a, b, c] = divs;
      console.log(a); // <div>1</div>的节点对象
  </script>
</body>

当然我们也可以自己实现迭代结构:

class MyIterator {
  constructor(obj) {
    this.data = obj;
  }
  [Symbol.iterator]() {
    let index = 0;
    let data= this.data;
    let keysArr = Object.keys(data || {});
    let length = keysArr.length;
    return {
      next() {
        if (index < length) {
          return {
            done: false,
            value: '解构:' + data[keysArr[index++]]
          }
        } else {
          return { done: true};
        }
      }
    }
  }
}

const myIterator = new MyIterator({a: 'value-a', b: 'value-b'});
const [a, b] = myIterator;

console.log(a); // 解构:value-a
console.log(b); // 解构:value-b

一个经典例子:交换两值

let a = 1;
let b = 2;

[a, b] = [b, a];

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

不知道有没有人和我一样,最开始疑惑:为什么a = b赋值完后,a变成了2,再b = a的话,那不就是b = 2了吗?
后来我换了种思考方式,我们应该把赋值左右两边区分开来,分别是赋值源和赋值目标,首先我们需要确定好赋值源的值,即[b ,a]等同于已经确定好了[2,1],然后再进行[a ,b] = [2,1]赋值。

对象的解构赋值

let { a, b } = { a: 1, b: 2 }; // a = 1, b = 2
let { a } = { b: 1, c: 2 }; // a = undefined
let { a: [b, { c }] } = { a: [1, { c: 2 }] }; // b = 1, c = 2

属性别名

let { a: hello, b } = { a: 1, b: 2 };
console.log(hello); // a失效,此时hello = 1
console.log(b); // 2

基础类型的解构

  • String:之前说了String类型是个迭代器结构,使用数组解构能得到对应位置的字符。而数组本质上也是个对象类型,因此String类型也能够使用对象解构。

  • Number/Boolean:遇到数字或者布尔值时,会转化成包装对象,然后再做对象解构。

let bool = true;

let { toString } = bool
console.log(toString.call(bool)); // 'true'

默认值

解构赋值可以添加默认值。

数组解构赋默认值

let [a, b = 2] = [1, undefined]; // x=1, y=2

需要注意的是ES6内部使用严格相等运算符(===)来判断一个值是否是undefined。如下例子,如果是null,则默认值失效,所以我们在写解构赋值默认值时需要注意自己的数据是否可能出现null。

let [a = 1, b = 2] = [undefined, null];
console.log(a); // 1
console.log(b); // null

对象解构赋默认值

同样与数组解构中判断undefined逻辑相同。

let { a = 1, b = 2} = {a: 3, b: null};
console.log(a); // 3
console.log(b); // null

如果又想使用别名,又想设置默认值,能同时写吗?答案是可以。

let { a: hello = 1, b = 2} = {a: 3, b: null};
console.log(hello); // a失效,此时hello = 3
console.log(b); // 2

参考