对象的浅拷贝

136 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

前端程序员,可能没有谁没没复制对象吧? 今天就一天来学习学习对象的复制。

对象,非彼对象

为什么要进行复制

什么时候需要进项对象的复制??

  1. 不破坏原数据 原始数据可能还需要在别处使用,如果进行了更改,可能导致意外的结果。 比如函数的参数,构造函数的参数等等。

函数式编程中有一个重要的概念,副作用, 无副作用,其中有一条就是不要修改入参。

  1. 需要基于当前数据,创建新的数据,用于他用。

浅复制

Object.assign

ES6新增的方法,大家都说是浅复制,其不能复制原型上的方法。

语法:

Object.assign(target, ...sources)

  • target: 目标对象, null, undefined会报错,其余均会先转为Object
  • sources: 多个源对象,任何数据均可, 非object类型会被忽视,

其实如果只有一级属性,且值都是非引用类型,他就是深复制。

var obj = {a:1, b:2};

var obj2 = Object.assign({}, obj);

就结果上说,是深复制哈。

其除了第一个参数,其余参数是null, undefined也不会报错,真的是比较贴心。

// null 和 undefined作为一个参数,报错
Object.assign(null, {a:1})  // Uncaught TypeError: Cannot convert undefined or null to object
Object.assign(undefined, {a:1})  // Uncaught TypeError: Cannot convert undefined or null to object

// 多个源对象
Object.assign({}, {a:1}, {b:2}) // {a:1, b:2}

// source非对象类型被忽视
Object.assign({}, null, undefined, 1, false, 10n, '')  // {}

// function 如果是有属性,也是会被复制的
function a(){}
a.bbbb = 1;
Object.assign({}, a);   // {bbbb: 1}

自己实现浅复制

我们也可以自己实现一个,这里直接借用MDN的代码,比较清晰和完善。 大致有三个注意点,代码中均标记出来了.

 function assign(target, varArgs) { // 
      'use strict';
      if (target === null || target === undefined) {
        throw new TypeError('Cannot convert undefined or null to object');
      }

      var to = Object(target); // 注意点1:先转为对象

      for (var index = 1; index < arguments.length; index++) {
        var nextSource = arguments[index];

        if (nextSource !== null && nextSource !== undefined) {
          for (var nextKey in nextSource) {  //  注意点2: for in 遍历
            // Avoid bugs when hasOwnProperty is shadowed
            // 注意点3: hasOwnProperty 判断是不是自身属性
            if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {  
              to[nextKey] = nextSource[nextKey];
            }
          }
        }
      }
      return to;
}

基本原理就是 for in 获取属性键,然后Object.prototype.hasOwnProperty判断是不是自有属性, 满足条件就进行复制,反之跳过。

这里就提一个问题:
Object.assign能否复制 Symbol类型的属性???

答案是支持, 而上面的MDN的代码中显然是不支持的? 为什么?
因为 for in 并不能遍历出Symbol类型的属性键。 你想想,你要自己实现 Object.assign前提肯定是其不被支持,既然不被支持,其不备支持,当然 Symbol也不会被支持,因为他们都是ES6才出现的啊。

小结

今天你收获了吗?