浅谈JavaScript深拷贝

前言

JavaScript的浅拷贝、深拷贝是一个老生常谈的话题,真正完美的深拷贝其实是比较困难的,但相对的能应用的场景也同样比较少,个人感觉浅拷贝和深拷贝的核心概念无非是对JavaScript引用类型的理解,普通的值类型可以直接复制,应用类型的直接复制则是对其地址的复制,这个其实就是C语言中的指针的概念。明白了这些深浅拷贝就不难理解了,接下来分享一些常用的操作。

浅拷贝

浅拷贝就是不考虑引用类型,我们把对象的属性或是数组的元素都当作原始类型来看待。

Object.assign() 
这个方法是用来合并两个对象的属性,将要拷贝的对象和一个空对象合并就能成为一个新的对象

cosnt temp = { a: 1, b: 2}
cosnt obj = Object.assign({}, temp)

temp === obj // false
es6扩展运算符 ...

cosnt temp = { a: 1, b: 2}
cosnt obj = { ...temp }
这种方法可以将temp里的属性提取出来放到新的对象里面

const array = [1,2,3,4,5]
cosnt newArray = [...array]
数组也是同理

深拷贝

深拷贝要比浅拷贝难实现的多,浅拷贝的方式会把引用类型的地址直接复制过去,不同的两个对象里面的引用类型的属性会互相影响,这种有时候不会符合我们的要求,此时就需要深拷贝。

JSON.parse(JSON.stringify())

这种配合使用算是比较常用的一种深拷贝方式了,将对象转成字符串,再重新生成对象,这样新生成的对象的引用类型和原对象里面的就不同了。

cosnt temp = { a: 1, b: 2, c: { d: 1 } }
cosnt obj = JSON.parse(JSON.stringif(temp))

temp === obj // false
temp.c === obj.c // false

这种方式的缺陷是undefined, 函数和对象内部循环引用的属性无法拷贝

MessageChannel

MessageChannel的介绍可以查看MDN,这个API其实是建立一个消息管道来进行页面通信使用的,使用这个API的比JSON的方法的优势在于可以进行有循环引用对象的拷贝,但是依然无法拷贝函数

function deepClone(obj) {
  return new Promise(resolve => {
    const { port1, port2 } = new MessageChannel()
    port2.onmessage = event => {
      return resolve(event.data)
    }
    port1.postMessage(obj)
  })
}

const temp = { a: 1, b: 2, c: { d: 1 } }
deepClone(temp).then(obj => {
    console.log(obj)
})

_.cloneDeep(value)

_.cloneDeep()是lodash库所提供的深拷贝函数,也是个人比较推荐的一种,基本可以满足深拷贝的要求,不需要我们再自己写工具类。

自定义工具类deeepClone

最后当然得自己实现一个,但是一个完美的深拷贝函数要考虑的东西太多了,比如要不要考虑原型链上的属性、Dom对象如何处理、函数如何处理等,所以我提供了一个不太完善的深拷贝函数,这个函数对于对象方法属性的深拷贝也是想了比较久才写出来的。

function deepClone(obj, parent) {
  function isObj(obj) {
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
  }
  function isFunction(obj) {
    return typeof obj === 'function'
  }
  if(!isObj(obj)) {
    return obj
  }

  if(isFunction(obj)) {
    return obj.bind(parent)
  }

  const newObj = Array.isArray(obj) ? [...obj] : {...obj}
  
  Reflect.ownKeys(obj).forEach(key => {
    newObj[key] = isObj(newObj[key]) ? deepClone(newObj[key], newObj) : newObj[key]
  })
  
  return newObj
}

let temp = { a: 123, b: { c: function () {return 3} } }
let obj = deepClone(temp)

结语

每写一篇文章感觉都是对知识的总结提炼,虽然没人看也要坚持下去。

如果有疏漏的地方,欢迎指出

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容