亮神知识库 亮神知识库
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)

亮神

前程明亮,未来可期
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)
  • 基础

  • 手写代码

    • 手写类型转换
    • 手写累加、累乘函数
    • 手写new
    • 手写深拷贝
    • 手写Object.create
    • 手写继承
    • 手写extends
    • 手写instanceof
    • 手写call、apply、bind
    • 手写jsonp
    • 手写getQueryString
    • 手写setInterval
    • 手写防抖与节流
    • 手写对象属性值迭代器
    • 手写分时函数
    • 手写事件委托
    • 手写图片懒加载
    • 手写原生Ajax请求
    • 手写AOP装饰函数
    • 手写柯里函数
    • 手写数组扁平化flat
    • 手写数组去重
    • 手写eventEmit类
    • 手写Vue数据响应式
    • 手写Vue nextTick
    • 手写Promise
  • JS底层深入

  • CSS

  • 基础
  • 手写代码
0zcl
2021-06-18

手写深拷贝

  • 数组的浅拷贝方式
  1. concat: const copy_arr = arr.concat()
  2. slice: const copy_arr = arr.slice()
  3. 解析赋值:const copy_arr = [...arr]
  • 对象的浅拷贝
  1. Object.assign(): const copy_obj = Object.assign({}, obj)
  2. 解析赋值:const copy_obj = {...obj}
  • 深拷贝
  1. JSON.parse(JSON.stringify(xxx))
  2. 自已实现一个
  3. 用第三方库

注意

JSON.parse(JSON.stringify(xxx)) 实现深拷贝,会有什么问题吗?

答: 原理:用<code>JSON.stringify</code>把JS对象转成<code>JSON字符串</code>,再用<code>JSON.parse</code>把JSON字符串转成 JS对象 <strong>存在问题</strong>

  1. 会忽略 <code>undefined</code>, <code>Symbol</code>。会忽略
  2. 不能序列化函数。会忽略
  3. 处理循环引用的对象会出错
  4. 不能正确处理new Date()。把对象变成了string
  5. 不能处理正则. 正则会变成空对象 {}
// 1 & 2
obj = {
  name : 'dayday',
  h1 : undefined,
  h2 : Symbol('dayday'),
  h3 : function () {},
}
JSON.parse(JSON.stringify(obj))
// {name: "dayday"}

// 3
// Uncaught TypeError: Converting circular structure to JSON

// 4
JSON.parse(JSON.stringify(new Date()))
// "2021-06-15T03:11:55.656Z"
JSON.parse(JSON.stringify(new Date()))
// "2021-06-15T03:11:56.231Z"

// 5
let demo = {
    name: "daydaylee",
    a: /'123'/
}
JSON.parse(JSON.stringify(demo))
// {
//   a: {}
//   name: "daydaylee"
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

注意

请实现浅拷贝

答:

function isObject(obj) {
  return typeof obj === 'object' && obj !== null
}
function shallowCopy(value) {
  if (!isObject(value)) return value
  const newValue = value instanceof Array ? [] : {}
  for (let key in value) {
    newValue[key] = value[key]
  }
  return newValue 
}

// 测试
newValue = shallowCopy(34)
// 34
newValue = shallowCopy([1,2,4])
// [1, 2, 4]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

注意

请实现深拷贝

答:

function isObject(obj) {
  return typeof obj === 'object' && obj !== null
}
function deepCopy(value) {
  if (!isObject(value)) return value
  const newValue = value instanceof Array ? [] : {}
  for (let key in value) {
    newValue[key] = isObject(value[key]) ? deepCopy(value[key]) : value[key]
  }
  return newValue
}
// 测试
newValue = deepCopy(34)  // 34
newValue = deepCopy([1,2,4])  // [1, 2, 4]
obj = { a: 1 }
obj2 = { b: 2, c: [1,2,4], d: obj }
newValue = deepCopy(obj2)
obj2.d.a = 22
obj // { a: 22 }
newValue // {b: 2, c: Array(3), d: {a: 1}}  注意这里已经实现深拷贝了,d对应的value没有变化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

存在问题:

  • 循环引用,导致爆栈

max_stack

注意

请实现深拷贝。如何解决循环引用导致的爆栈问题?

原理:设置一个数组或者哈希表存储已拷贝过的对象,当检测到当前对象已存在于哈希表中时,取出该值并返回即可

答:

function isObject(obj) {
  return typeof obj === 'object' && obj !== null
}
function deepCopy2(value, hash = new WeakMap()) {
  // 终止条件
  if (!isObject(value)) return value
  if (hash.has(value)) return hash.get(value) // 哈希表找得到
  const newValue = value instanceof Array ? [] : {}
  hash.set(value, newValue) // 找不到,需添加到哈希
  for (let key in value) {
    newValue[key] = isObject(value[key]) ? deepCopy2(value[key], hash) : value[key]
  }
  return newValue
}
// 测试
var a = {
  name: "muyiy",
  book: {
      title: "You Don't Know JS",
      price: "45"
  },
  a1: undefined,
  a2: null,
  a3: 123
}
a.circleRef = a;
var b = cloneDeep3(a);
console.log(b);
// {
// 	name: "muyiy",
// 	a1: undefined,
//	a2: null,
// 	a3: 123,
// 	book: {title: "You Don't Know JS", price: "45"},
// 	circleRef: {name: "muyiy", book: {…}, a1: undefined, a2: null, a3: 123, …}
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

使用WeakMap的好处?为什么不使用Map?

答:WeakMap是弱引用,Map是强引用。比如说:

const weakMap = new WeakMap()
let obj = { name: zcl }
weakMap.set(obj, obj)
obj = null
1
2
3
4

weakMap 和 obj 是弱引用关系。当下一次垃圾回收机制执行时,obj对象占用的内存就会被释放掉。

注意

能否不用递归,用循环实现?

面试说:当我提出用循环来实现时,基本上90%的前端都是写不出来的代码的,这其实让我很震惊。

我:不服,让我来试试

答:

不会。

参考: 深拷贝的终极探索(99%的人都不知道) (opens new window) 面试题之如何实现一个深拷贝 (opens new window)

编辑 (opens new window)
上次更新: 2025/07/20, 06:21:22
手写new
手写Object.create

← 手写new 手写Object.create→

最近更新
01
2024年
07-20
02
2023年
07-20
03
2022年
07-20
更多文章>
Theme by Vdoing | Copyright © 2025-2025 亮神 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式