浅拷贝和深拷贝

浅拷贝

拷贝出来的目标对象有着与原对象相同的属性值。如果我们的对象中嵌套了对象,那么像 object.assign 和扩展运算符(...)这样的浅层复制机制将只创建根级对象的副本,但深层对象仍将被共享。

新对象赋值

最直接的方式是新定义对象,在对其赋值:

let md = { name: 'lio', url: 'lio-zero.github.io' }
let obj = {
  name: md.name,
}

obj.name = 'lion'
console.log(md) // { name: "lio", url: "lio-zero.github.io" }

操作对象,上面方法显然不太灵活,这时使用循环,复制过程需要改变一些数据,可以使用这种方法

let md = { name: 'lio', url: 'lio-zero.github.io' }
let obj = {}

for (const key in md) {
  obj[key] = md[key]
}

obj.name = 'lion'
console.log(md) // { name: "lio", url: "lio-zero.github.io" }

使用 concat 方法

  • concat 方法用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
let arr = [1, 2, 3]
const arr2 = arr.concat()

arr2[1] = 23
console.log(arr) // [1, 2, 3]

使用 slice 方法

  • sliceconcat 的用法类似。
let arr = [1, 2, 3]
const arr2 = arr.concat()

arr2[1] = 23
console.log(arr) // [1, 2, 3]

使用 Object.assign 合并

let md = { name: 'lio', url: 'lio-zero.github.io' }
let obj = Object.assign({}, md)

obj.name = 'lion'
console.log(md) // { name: "lio", url: "lio-zero.github.io" }

使用 ... 扩展运算符

更加简便的方式是使用 ES6 的扩展运算符(...

let md = { name: 'lio', url: 'lio-zero.github.io' }
let obj = { ...md }

obj.name = 'lion'
console.log(md) // { name: "lio", url: "lio-zero.github.io" }

浅拷贝存在的问题

就向刚一开始说的,上面都存在一个问题,假如对象中存在更深层级的对象,这些较深的对象一旦发生了改变,则原对象和引用原数据的对象都将改变。

const arr = [1, 2, { name: 'K.O' }]
const arr2 = arr.concat()

arr2[2].name = 'O.K'
console.log(arr) // [1, 2, { name: 'O.K'}]
console.log(arr2) // [1, 2, { name: 'O.K'}]

可以看到,我们访问了 arrarr2 都改变了。

这时,我们就需要深度创建一个对象的副本。

深拷贝

深拷贝:拷贝出来的目标对象有着与原始对象相同的属性值,且嵌套的对象更改也不会影响到原始对象的改变,也就是它们之间有着相同的数据,但数据存储在不同的内存地址中。

JSON.stringify/parse

如果对象足够简单,我们可以使用 JSON.stringify 将其转换为字符串,然后使用 JSON.parse 将其转换回 JavaScript 对象。

let obj = {
  name: 'MSN',
  user: {
    name: 'BBC'
  },
  arr: [1, 2, 3]
}
let newObj = JSON.parse(JSON.stringify(obj))
newObj.user.name = 'K.O'


console.log(JSON.stringify(obj, null, 2))
/*
{
  "name": "MSN",
  "user": {
    "name": "BBC"
  },
  "arr": [
    1,
    2,
    3
  ]
}
*/
console.log(JSON.stringify(newObj, null, 2))
/*
{
  "name": "MSN",
  "user": {
    "name": "K.O"
  },
  "arr": [
    1,
    2,
    3
  ]
}
*/

可以看到,我们成功了,但这是有局限性的。如果您的对象有任何不能用 JSON 字符串表示的数据(例如函数)。那么这些数据将丢失!

其他情况还有:

  • 会忽略 undefinedsymbol 类型
  • 不能解决循环引用的对象

递归

更好的做法是递归调用浅拷贝,把所有属于对象的属性类型都遍历赋给另一个对象。

考虑:对象多层次的赋值方式,考虑其中存在数组情况

function deepCopy(obj) {
  let res = obj instanceof Array ? [] : {}
  for (const [k, v] of Object.entries(obj)) {
    res[k] = typeof v == 'object' ? deepCopy(v) : v
  }
  return res
}

let newObj = deepCopy(obj)
newObj.user.name = 'K.O'
newObj.arr.push(4)

console.log(JSON.stringify(obj, null, 2))
/*
{
  "name": "MSN",
  "user": {
    "name": "BBC"
  },
  "arr": [
    1,
    2,
    3
  ]
}
*/
console.log(JSON.stringify(newObj, null, 2))
/*
{
  "name": "MSN",
  "user": {
    "name": "K.O"
  },
  "arr": [
    1,
    2,
    3,
    4
  ]
}
*/

其他方法

  • 使用函数库 lodash 的 _.cloneDeep 方法
  • JQuery 的 JQuery.extend() 方法

更多资料

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

推荐阅读更多精彩内容

  • 在JavaScript中,对于Object和Array这类引用类型值,当从一个变量向另一个变量复制引用类型值时,这...
    啊灿2580阅读 171评论 0 0
  • javaScript的变量类型 javaScript的变量类型基本类型:引用类型: 浅拷贝和深拷贝的区分 浅拷贝浅...
    席坤阅读 227评论 0 0
  • 之前,我们讲过js的数据类型与堆内存、栈内存以及数据类型,了解该知识点以后,我们来进一步学习 浅拷贝与深拷贝。 在...
    YINdevelop阅读 587评论 0 0
  • 关于浅拷贝和深拷贝的介绍 浅拷贝 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。 ...
    陈成熟阅读 349评论 0 0
  • 浅拷贝和深拷贝 基础知识 数据分为基本数据类型(String, Number, Boolean, Null, Un...
    王逵_e9b3阅读 293评论 0 0