js拷贝

浅拷贝

Object.assign(target, ...source)

const target = {}
const source = {a: 1, b: {c: 2}}
Object.assign(target, source)
target.a = 3
target.b.c = 4
console.log(target) // { a: 3, b: { c: 4 } }
console.log(source) // { a: 1, b: { c: 4 } }  
Object.assign不会拷贝对象的继承属性
Object.assign不会拷贝对象的不可枚举的属性
可以拷贝Symbol类型的属性

扩展运算符 ...

const source = {a: 1, b: {c: 2}};
const target = {...source};
target.a = 3;
target.b.c = 4;
console.log(target); // { a: 3, b: { c: 4 } }
console.log(source); // { a: 1, b: { c: 4 } }

数组的concat方法

let arr = [1, [21, 22]]
let newArr = arr.concat()
newArr[0] = 2
newArr[1][0] = 31
console.log(arr) // [ 1, [ 31, 22 ] ]
console.log(newArr) // [ 2, [ 31, 22 ] ]
只能处理首层数据为数组数据

数组的slice方法

let arr = [1, [21, 22]]
let newArr = arr.concat()
newArr[0] = 2
newArr[1][0] = 31
console.log(arr) // [ 1, [ 31, 22 ] ]
console.log(newArr) // [ 2, [ 31, 22 ] ]
只能处理首层数据为数组的数据

自己实现方法shallowClone

const source = { a: 1, b: {c: 2}, d: undefined, e: null, f: () => {}, g: Symbol(), h: new Date(), i: RegExp(), j: NaN, k: Infinity, l: -Infinity}
const target = JSON.parse(JSON.stringify(source))
console.log(target) // { a: 1, b: { c: 2 }, e: null, h: '2024-12-01T15:09:04.548Z', i: {}, j: NaN, k: Infinity, l: -Infinity}
console.log(source) // { a: 1, b: { c: 2 }, d: undefined, e: null, f: [Function: f], g: Symbol(), h: 2024-12-01T15:09:04.548Z, i: /(?:)/, j: null, k: mull, l: null}

深拷贝

JSON.stringify()

const source = { a: 1, b: {c: 2}, d: undefined, e: null, f: () => {}, g: Symbol(), h: new Date(), i: RegExp(), j: NaN, k: Infinity, l: -Infinity}
const target = JSON.parse(JSON.stringify(source))
console.log(target) // { a: 1, b: { c: 2 }, e: null, h: '2024-12-01T15:09:04.548Z', i: {}, j: NaN, k: Infinity, l: -Infinity}
console.log(source) // { a: 1, b: { c: 2 }, d: undefined, e: null, f: [Function: f], g: Symbol(), h: 2024-12-01T15:09:04.548Z, i: /(?:)/, j: null, k: mull, l: null}
使用JSON.stringify会造成值为undefined,function,Symbol的键值对丢失;值为Date类型的数据会变成字符串;无法拷贝不可枚举属性;无法拷贝对象的原型链;拷贝RegExp类型会变成空对象;对象中含有NaN,Infinity,-Infinity时,对应键的值会转为null;无法拷贝对象的循环应用,即对象成环[obj[key]]=obj

简单深拷贝

let source = {a: 1, b: {c: 2}, d: [1, 2]}
function deepClone(param) {
  let cloneParam
  // 首先,判断输入的值为对象还是数组还是其他类型
  if (checkOfData(param) === 'object') {
    cloneParam = {}
    // 对象使用for in循环,将对象内的值再进行递归调用
    for (let i in param) {
      cloneParam[i] = deepClone(param[i])
    }
  } else if (checkOfData(param) === 'array') {
    cloneParam = []
    // 数字使用for循环,将数组内的值再进行递归调用
    for (let i = 0; i < param.length; i++) {
      cloneParam[i] = deepClone(param[i])
    }
  } else {
    // 普通值直接进行赋值
    cloneParam = param
  }
  return cloneParam
}
const target = deepClone(source)
target.b.c = 3
target.d[0] = 4
console.log(target) // { a: 1, b: { c: 3 }, d: [ 4, 2 ] }
console.log(source) // { a: 1, b: { c: 2 }, d: [ 1, 2 ] }
只对对象,数组做处理,其他不做处理

改进版深拷贝

const isComplexDataType = (obj) => (typeof obj === 'object' || typeof obj === 'function') && obj != null
const deepClone = function (obj, hash = new WeakMap()) {
  if (obj.constructor === Date) return new Date(obj) // 日期对象返回一个新日期
  if (obj.constructor === RegExp) return new RegExp(obj) // 正则对象返回一个新的正则对象
  if (hash.has(obj)) return hash.get(obj) // 循环引用就用weakMap来解决
  let allDesc = Object.getOwnPropertyDescriptor(obj)
  // 遍历出入参数的所有键的特性
  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
  // 继承原型链
  hash.set(obj, cloneObj)
  for (let key of Reflect.ownKeys(obj)) {
    cloneObj[key] =
      isComplexDataType(obj[key]) && typeof obj[key] !== 'function'
        ? deepClone(obj[key], hash)
        : obj[key]
  }
  return cloneObj
}
let source = { a: 1, b: {c: 2}, d: undefined, e: null, f: () => {}, g: Symbol(), h: new Date(), i: RegExp(), j: NaN, k: Infinity, l: -Infinity, [Symbol('1')]: 1}
Object.defineProperty(source, 'innumberable', { enumerable: false, value: '不可枚举属性' })
source = Object.create(source, Object.getOwnPropertyDescriptors(source))
source.loop = source // 设置loop成循环引用的属性
const target = deepClone(source)
target.b.c = 4
console.log(source) // { a: 1, b: { c: 2 }, d: undefined, e: null, f: [Function: f], g: Symbol(), h: 2024-12-03T03:11:20.305Z, i: /(?:)/, j: NaN, k: Infinity, l: -Infinity, loop: [Circular *1], [Symbol(1)]: 1}
console.log(target) // { a: 1, b: { c: 4 }, d: undefined, e: null, f: [Function: f], g: Symbol(), h: 2024-12-03T03:11:20.305Z, i: /(?:)/, j: NaN, k: Infinity, l: -Infinity, loop: [Circular *1], [Symbol(1)]: 1}

支持所有类型拷贝,包括对象成环

posted on 2024-12-05 08:22  shenhf  阅读(7)  评论(0编辑  收藏  举报

导航