0

0

0

修罗

站点介绍

只有了解事实才能获得真正的自由

js深拷贝、Object.create

修罗 2021-12-21 2286 5条评论 JS

首页 / 正文

js深拷贝

实现深拷贝

定义方法deepClone,参数为对象类型(除几个基本类型外都是对象类型)

const deepClone = function (obj, hash = new WeakMap()) {
  if (obj.constructor === Date) {
    return new Date(obj) // 日期对象直接返回一个新的日期对象
  }
  
  if (obj.constructor === RegExp){
    return new RegExp(obj) //正则对象直接返回一个新的正则对象
  }
  
  // 如果循环引用了就用 weakMap 来解决
  if (hash.has(obj)) {
    return hash.get(obj)
  }

  // 浅拷贝对象
  let allDesc = Object.getOwnPropertyDescriptors(obj)
  // 遍历传入参数所有键的特性
  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)

  // map存当前对象,obj必须为对象类型,因为WeakMap键必须是对象类型
  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
}

// 判断是不是Object类型
const isComplexDataType = obj => typeof obj === 'object' && (obj !== null)
  • 定义方法传入对象,遍历属性拷贝到另一个对象,使用 Reflect.ownKeys 方法能够遍历对象的不可枚举属性以及 Symbol 类型;
  • 当参数为 Date、RegExp 类型,则直接生成一个新的实例返回;
  • Object.create 方法创建一个新对象,并继承传入原对象的原型链;利用 ObjectgetOwnPropertyDescriptors 方法可以获得对象的所有属性描述,再传给Object.create第二个参数,拷贝原对象。
  • 利用 WeakMap 类型存储拷贝对象,如果存在循环,则引用直接返回 WeakMap 存储的值;因为 WeakMap 是弱引用类型,可以有效防止内存泄漏。

测试

let obj = {
  num: NaN,
  str: '',
  boolean: false,
  undef: undefined,
  nul: null,
  obj: { name: 'huiyani', age: 20, desc: { hobby: "打电动" } },
  arr: [1, 2, 3],
  func: function () { console.log('huiyani') },
  date: new Date(),
  reg: new RegExp('/huiyani/ig'),
  [Symbol('huiyani')]: "huiyani",
};

Object.defineProperty(obj, 'innumerable', {
  enumerable: false, value: '不可枚举属性' }
);

obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj // 设置loop成循环引用的属性

let cloneObj = deepClone(obj)
cloneObj.arr.push(4)
cloneObj.obj.desc.hobby = "又在打电动"

console.log('obj', obj)
console.log('cloneObj', cloneObj)

我们看一下结果,cloneObjobj 的基础上进行了一次深拷贝,cloneObj 里的 arr 数组和obj对象进行了修改,并未影响到 obj.arrcloneObj.obj对象的变化,如下图所示

1640019870611.png

Object.create

1、参数传对象,返回一个对象,原型指向传入对象

let obj = Object.create({a:1})

console.log(obj)

1640016435320.png

2、传null,返回一个没有原型的对象

let obj1 = Object.create(null)

console.log(obj1)

1640016499615.png

3、传入属性描述符对象,默认属性描述配置为false

let obj2 = Object.create({}, { p: { value: 42 } })

console.log(obj2)

1640016706957.png

获取返回对象的属性描述符

console.log(Object.getOwnPropertyDescriptors(obj2))

1640016731035.png

writable:属性的值是否能够被重新赋值

configurable:是否可以删除该属性

enumerable:此属性是否可以被枚举(使用for…in或Object.keys())

4、配置属性描述符

let des = { p: { value: 42, writable: true, enumerable: true, configurable: true } }

let obj3 = Object.create({}, des)

console.log(Object.getOwnPropertyDescriptors(obj3))

1640016954767.png

5、create方法第二个参数为一个对象的属性描述符

let obj4 = { a:1, b:2 }
let obj5 = { c:3, d:4 }

let obj6 = Object.create(obj4, Object.getOwnPropertyDescriptors(obj5))

console.log(obj6)
console.log(Object.getOwnPropertyDescriptors(obj6))

1640017156299.png
1640017206825.png

6、第一个参数传入类型决定返回类型

let desc = Object.getOwnPropertyDescriptors([1,2,3])
// 传入对象返回对象
let clone = Object.create({}, desc) // clone: {0: 1, 1: 2, 2: 3, length: 3}
// 传入数组返回数组
let clone = Object.create([], desc) // clone: [1, 2, 3]

评论(5)

  1. 1 游客 2025-09-01 11:27 回复

    1

  2. 1 游客 2025-09-01 12:13 回复

    1

  3. @@rHrYe 游客 2025-09-01 12:15 回复

    1

  4. 1 游客 2025-09-01 12:17 回复

    1

  5. 1 游客 2025-09-01 12:18 回复

    1


最新评论

  • 1

    1

  • 1

    1

  • -1' OR 2+158-158-1=0+0+0+1 or 'TKCTZnRa'='

    1

  • 1

    1

  • 1

    1

  • 1

    1

  • 1

    1

  • @@5Qa2D

    1

  • 1

    1

  • 1

    1

日历

2025年09月

 123456
78910111213
14151617181920
21222324252627
282930    

文章目录

推荐关键字: Linux webpack js 算法 MongoDB laravel JAVA jquery javase redis