对象为什么能解构?让我来告诉你😏😏😏

3,603 阅读3分钟

本文正在参加「金石计划」

昨晚在回家的路上看到一篇文章,我看到了文章底下有这样的一个评论:既然对象没有Symbol.iterator方法,那么为什么对象也能实现解构呢?

本来也是随便逛逛掘金的,没想到被这个评论给吸引了,因为我想到我曾经也和一位朋友探讨过相关的问题,当然那时候只是简单的谈了一下,并没有深入去了解一下。

image.png

那么今天这篇文章我们就来聊聊对象没有Symbol.Iterator方法是如何实现对象结构的,那么我给到的答案就是: 因为它能对象结构啊,所以能结构啊

不废话了,进入正题吧,如果你不了解 Symbol.Iterator 方法是什么,那么你可以看看我之前写的这篇文章里有讲到,你可以到这里进行学习 一文带你彻底搞懂JavaScript异步编程

解构的本质

在这里就不再对解构的一些语法进行讲解了,如果有需要就自行去 MDN 进行查阅吧,那里讲得更详细。

我们先来看一个demo,我们定义有如下的对象,代码如下所示:

const obj = {
  nickname: 7,
  age: 18,
  address: "西安",
};

let { nickname, ...reset } = obj;

我们通过控制台输出 nicknane 的值为 7,而 reset 的值为一个对象:

{
  age: 18,
  address: "西安",
}

我们通过断点调试去查看变量的值也正是发现它会复制给一个新变量,这个变量名也就是左边解构出来的字段,如下图所示:

image.png

又因为这是一个新的赋值,所以你修改解构出来的值也并不会改变源对象的值,因为这已经是一个全新的值,请看代码:

const obj = {
  nickname: 7,
  age: 18,
  address: "西安",
};

let { nickname, ...rest } = obj;
console.log(obj);
console.log(nickname);
nickname = 777;
console.log(obj);
console.log(nickname);

image.png

哟,看到上图的输出了吧,对象原样输出,修改 nickname 只会改变外部变量 nickname 的值,并不会改变源对象的值。

相面的对象解构,实际上是经过了这样的过程:

let nickname = obj.nickname;

剩余参数解构原理

在上面中讲到对象解构的原理确实很简单,但是剩余参数这里做的事情比较多,但是并不是说很难,我们通过 Babel 可以查看到这个事情的本质,先来看一个demo:

const obj = {
  nickname: 7,
  age: 18,
  address: "西安",
  [Symbol("test")]: "111",
};

const { nickname, ...rest } = obj;

我们将这些代码将去放去 Babel 进行代码编译,最终代码会被编译成以下代码:

function _objectWithoutProperties(source, excluded) {
  if (source == null) return {};
  var target = _objectWithoutPropertiesLoose(source, excluded);
  var key, i;
  if (Object.getOwnPropertySymbols) {
    var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
    console.log(sourceSymbolKeys);
    for (i = 0; i < sourceSymbolKeys.length; i++) {
      key = sourceSymbolKeys[i];
      if (excluded.indexOf(key) >= 0) continue;
      if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
      target[key] = source[key];
    }
  }
  return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {
  if (source == null) return {};
  var target = {};
  var sourceKeys = Object.keys(source);
  var key, i;
  for (i = 0; i < sourceKeys.length; i++) {
    key = sourceKeys[i];
    if (excluded.indexOf(key) >= 0) continue;
    target[key] = source[key];
  }
  console.log(target);
  return target;
}

var obj = {
  nickname: 7,
  age: 18,
  address: "西安",
  [Symbol("test")]: "111",
};

var nickname = obj.nickname,
  rest = _objectWithoutProperties(obj, ["nickname"]);

上面这段代码主要做的事情是有如下几点:

  1. 调用 _objectWithoutProperties 函数,并传入 obj 对象和前面已经解构过的值;
  2. 在该函数中调用 _objectWithoutPropertiesLoose 函数获取到剩余的属性或者方法;
  3. 因为 Object.keys 方法无法遍历 Symbol 属性,所以又继续调用 Object.getOwnPropertySymbols 方法获取剩下的 Symbol 属性;
  4. 最后生成一个新的对象并返回;

所以最终总结成一句话就是,对象的解构就是: 创建新变量 -> 枚举属性 -> 复制属性并赋值。