JavaScript 之深浅拷贝

2019-11-08  本文已影响0人  临安linan

更多个人博客:(https://github.com/zenglinan/blog)

如果对你有帮助,欢迎star。

对象拷贝是经常会遇到的场景, 本文会总结几种深浅拷贝的方法

一. 浅拷贝

1. 浅拷贝的定义

先来说说什么是浅拷贝, 首先要明确一点:

直接拷贝对象的引用这不叫浅拷贝

先来看一个案例:

拷贝引用:

var obj1 = {
  a: 1,
  b: {
    c: 1
  }
}

var obj2 = obj1  // 拷贝 obj1 的引用

obj2.a = 2
console.log(obj1.a)  // 2

浅拷贝:

var obj1 = {
  a: 1,
  b: {
    c: 1
  }
}

var obj2 = {...obj1}

obj2.a = 2
console.log(obj1.a)  // 1

上面可以看到, 直接复制引用时, 两个对象实际保存的是堆中同一个对象的引用, 而浅拷贝则不同, 浅拷贝重新生成了一个新的对象, 但是浅拷贝只会拷贝一层, 假如原对象的属性值为对象, 也只会拷贝这个对象的引用。

也就是说: 在浅拷贝的对象中, 修改基本类型的值不会影响原数据, 修改复杂类型时, 会影响原数据

2. 浅拷贝的实现

(1) ...扩展运算符

var copy = {...obj} 

ES6 中提供了扩展运算符, 可以遍历取出对象身上的属性, 分配到新的对象上

(2) Object.assign

var copy = {}
Object.assign(copy, obj)

Object.assign 可以将第二个对象里可枚举的属性复制到第一个对象里

(3) for...in

function shallowCopy(src){
  let result = {}
  for(let prop in src){
    if(src.hasOwnProperty(prop)){
      result[prop] = src[prop]
    }
  }
  return result
}

注意: for...in 循环会遍历原型链上所有的的属性, 浅拷贝只需拷贝对象身上的属性。

所以加一层 hasOwnProperty 判断

二. 深拷贝

1. 深拷贝的定义

与浅拷贝相比, 深拷贝也会对对象的子对象进行拷贝

当修改拷贝对象上的对象属性时, 不会对源对象产生影响

2. 深拷贝的实现

(1) JSON序列化与反序列化

最先说的办法当然是家喻户晓的 JSON 序列化与反序列化方法啦~

var copy = JSON.parse(JSON.stringify(src))

先序列化一下对象变成字符串, 然后再反序列化成对象

这个办法有两个缺陷:

  1. 既然是 JSON 对象上的方法, 当然不支持拷贝函数了o(╥﹏╥)o

  2. 同样的道理, JSON 里面没有 Symbol 类型, 自然也不支持 Symbol 类型的键

但是这个方法最简单了, 一行代码...

(2) for...in + 递归

function deepClone(src){
  if(typeof src !== "object") return src

  let result = Array.isArray(src) ? [] : {}

  for(let prop in src){
    if(!src.hasOwnProperty(prop)) continue
    let value = src[prop]
    result[prop] = typeof value === 'object' ? deepClone(value) : value
  }
  return result
}

这个方法比 JSON 序列化的方法更完善的一点是: 支持拷贝函数

但是依然无法拷贝键为 Symbol 类型的属性

原因是: for...in 无法遍历到 Symbol 类型的属性

(3) Reflect.ownKeys() + 递归

MDN 上是这样描述的:

Reflect.ownKeys 方法返回一个由目标对象自身的属性键组成的数组。它的返回值等同于

Object.getOwnPropertyNames(target)
.concat(Object.getOwnPropertySymbols(target))

这个方法可以取到对象自身的所有属性, 包括 Symbol 类型的属性

function deepClone(src){
  if(typeof src !== 'object') return src

  let result = Array.isArray(src) ? [] : {} 
  Reflect.ownKeys(src).forEach((key) => {
    let value = src[key]
    result[key] = typeof value === 'object' ? deepClone(value) : value
  })
  return result
}
上一篇 下一篇

猜你喜欢

热点阅读