对象和数组使用展开运算符的区别

537 阅读2分钟

展开运算符是什么

展开运算符,也叫扩展运算符,常用...表示,一般用来复制、拼接、修改数组或对象。举个栗子:

const arr1 = [0, 1, 2]
const arr2 = [3, 4, 5]
const arr3 = [...arr1, ...arr2] //[0,1,2,3,4,5]

const obj1 = {a:1}
const obj2 = {b:2}
const obj3 = {...obj1, ...obj2} //{a:1, b:2}

一个误会———必须要具有iterator

在阮一峰的ES6教程关于iterator的应用里有这么一个段话: 第一次看的时候,误解为展开运算符的使用要依靠iterator来实现。然而不是这样的。真实的情况应该是,数组使用展开运算符确实需要iterator接口,但是对象使用的时候调用机制不同,不依赖于iterator。

浅析

首先,通过babel来测试一下它是怎么实现的:

// 编译前:
function test() {
  const obj = { a1 };
  const arr = [123];
  console.log([...arr]);
  console.log({ ...obj });
  debugger;
}
// babel编译后:
("use strict");

var _interopRequireDefault = require("./node_modules/@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports"__esModule", {
  valuetrue
});
exports.test = test;

require("core-js/modules/es7.object.get-own-property-descriptors");

require("core-js/modules/web.dom.iterable");

require("core-js/modules/es6.object.keys");

var _defineProperty2 = _interopRequireDefault(
  require("./node_modules/@babel/runtime/helpers/defineProperty")
);

function ownKeys(object, enumerableOnly) {
  var keys = Object.keys(object);
  if (Object.getOwnPropertySymbols) {
    var symbols = Object.getOwnPropertySymbols(object);
    if (enumerableOnly)
      symbols = symbols.filter(function(sym) {
        return Object.getOwnPropertyDescriptor(object, sym).enumerable;
      });
    keys.push.apply(keys, symbols);
  }
  return keys;
}

function _objectSpread(target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i] != null ? arguments[i] : {};
    if (i % 2) {
      ownKeys(Object(source), true).forEach(function(key) {
        (0, _defineProperty2.default)(target, key, source[key]);
      });
    } else if (Object.getOwnPropertyDescriptors) {
      Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
    } else {
      ownKeys(Object(source)).forEach(function(key) {
        Object.defineProperty(
          target,
          key,
          Object.getOwnPropertyDescriptor(source, key)
        );
      });
    }
  }
  return target;
}

function test() {
  var obj = {
    a1
  };
  var arr = [123];
  console.log([].concat(arr));
  console.log(_objectSpread({}, obj));
  debugger;
}

上面这段代码写了数组和对象使用展开运算符后babel编译结果。可以看出,同样的展开运算符,数组和对象的处理方法是不一样的。数组中的展开运算符用过concat实现,而对象就是通过循环遍历得到。 在MDN中关于展开运算符可以看到这样一个描述: 也就是说,对象内使用展开运算符拷贝合并属性,是es的特性。

总结

数组内使用展开运算符要调用iterator接口,而对象内使用展开运算符是es标准中赋予的特性,它就是能够被展开。