精简版深拷贝

1,372 阅读2分钟

深拷贝TS转译核心--Object.slice()

/* 深拷贝 */
var a = [1,2,3]
var b = [...a] // ES6 数组展开

var c = {'a':1,'b':2}
var d = {...c} // ES2018 新增对象展开

// 展开出的内容都是深拷贝内容
// 一个完备的深拷贝,往往需要考虑数组内是否含有对象的情况
// 网上垃圾教程太多,最好自行理解一下,掌握最简写法
// 下面是 tsc 对本文件 .ts 的转译 .js(转译至ES5版)
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) 
                t[p] = s[p];
        }
        return t;
    };  
    return __assign.apply(this, arguments);
};
var a = [1, 2, 3]; 
var b = a.slice();
var c = { 'a': 1, 'b': 2 };
var d = __assign({}, c);

有关知识点:

展开语法,迭代器(Symbol.iterator),call和apply绑定,function arguments,||(短路或),对象属性查询,数组slice用法

我的最简写法:

Object.assign() FF34+

// 针对普通数组对象的拷贝
var deepcopyarr = (...arr) => Object.assign([], arr.reduce((acc,cur) => [...acc, ...cur]))
var deepcopyobj = (...obj) => Object.assign({}, ...obj)

测试用例

var a0 = {'a':1}
var a1 = deepcopyobj(a0)
a0.a = 2
console.log(a0, a1) // {'a':2} {'a':1}

var b0 = {'a':1}
var b1 = {'b':2}
var b2 = deepcopyobj(b0,b1)
console.log(b2) // {'a':1,'b':2}

var c0 = [1,2,3]
var c1 = deepcopyarr(c0)
c0 = [4,5,6]
console.log(c0, c1) // [4,5,6] [1,2,3]

var d0 = [1,2,3]
var d1 = [3,4,5]
var d2 = deepcopyarr(d0,d1)
console.log(d2) // [1,2,3,3,4,5]

MDN上关于拷贝访问器

const obj = {
  foo: 1,
  get bar() {
    return 2;
  }
};

let copy = Object.assign({}, obj); 
console.log(copy); // { foo: 1, bar: 2 } copy.bar的值来自obj.bar的getter函数的返回值

下面这个函数会拷贝所有自有属性的属性描述符

function completeAssign(target, ...sources) {
  sources.forEach(source => {
    let descriptors = Object.keys(source).reduce((descriptors, key) => {
      descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
      return descriptors;
    }, {});

    // Object.assign 默认也会拷贝可枚举的Symbols
    Object.getOwnPropertySymbols(source).forEach(sym => {
      let descriptor = Object.getOwnPropertyDescriptor(source, sym);
      if (descriptor.enumerable) {
        descriptors[sym] = descriptor;
      }
    });
    Object.defineProperties(target, descriptors);
  });
  return target;
}

copy = completeAssign({}, obj);
console.log(copy);
// { foo:1, get bar() { return 2 } }

LESS IS MORE!