|
| 1 | +曾在项目中遇到过使用深拷贝,萌新的我,居然傻傻把地址引用当做深拷贝,南辕北辙。在朋友的帮助下知道了深拷贝可以简单粗暴一句话解决 |
| 2 | + |
| 3 | +``` |
| 4 | +JSON.parse(JSON.stringify(source)); |
| 5 | +``` |
| 6 | +`js` 中的引用类型和基本类型复制傻傻分不清的原因吧,堆内存,栈内存也不知道,一定是个假前端。 |
| 7 | + |
| 8 | +强烈推荐一篇文章 [深拷贝的终极探索(90%的人都不知道)](https://juejin.im/post/5bc1ae9be51d450e8b140b0c),原有一天我们成为那10%。哈哈 |
| 9 | + |
| 10 | +> 深拷贝和浅拷贝 |
| 11 | +
|
| 12 | +深拷贝和浅拷贝都是针对的引用类型,`JS` 中的变量类型分为值类型(基本类型)和引用类型;对值类型进行复制操作会对值进行一份拷贝,而对引用类型赋值,则会进行地址的拷贝,最终两个变量指向同一份数据 |
| 13 | +``` |
| 14 | +let arr1 = [{ |
| 15 | + label: '一级 1', |
| 16 | + children: [{ |
| 17 | + label: '二级 1-1', |
| 18 | + children: [{ |
| 19 | + label: '三级 1-1-1' |
| 20 | + }] |
| 21 | + }] |
| 22 | +}, { |
| 23 | + label: '一级 2', |
| 24 | + children: [{ |
| 25 | + label: '二级 2-1', |
| 26 | + children: [{ |
| 27 | + label: '三级 2-1-1' |
| 28 | + }] |
| 29 | + }, { |
| 30 | + label: '二级 2-2', |
| 31 | + children: [{ |
| 32 | + label: '三级 2-2-1' |
| 33 | + }] |
| 34 | + }] |
| 35 | +}, { |
| 36 | + label: '一级 3', |
| 37 | + children: [{ |
| 38 | + label: '二级 3-1', |
| 39 | + children: [{ |
| 40 | + label: '三级 3-1-1' |
| 41 | + }] |
| 42 | + }, { |
| 43 | + label: '二级 3-2', |
| 44 | + children: [{ |
| 45 | + label: '三级 3-2-1' |
| 46 | + }] |
| 47 | + }] |
| 48 | +}] |
| 49 | +let arr2 = arr1//arr1 和arr2指向了同一个地址 |
| 50 | +
|
| 51 | +arr1[1].children[0].children[0].label = "我被修改了" |
| 52 | +
|
| 53 | +arr1 === arr2 // true arr1 和 arr2 复制的是地址,而不是是值 |
| 54 | +
|
| 55 | +arr1 = []//arr1 指向一个新的地址 |
| 56 | +
|
| 57 | +arr1 == arr2 //false |
| 58 | +
|
| 59 | +``` |
| 60 | + |
| 61 | +> 引用类型的深层拷贝,深层拷贝是拷贝创建另一空间 |
| 62 | +``` |
| 63 | +let arr3 = JSON.parse(JSON.stringify(arr1)) |
| 64 | +arr1 === arr3 //false,他们虽然长的一模一样,但是不是同一个地址 |
| 65 | +
|
| 66 | +arr1[1].children[0].children[0].label = "我又被修改了" // arr1和arr3 现在长得也不一样了,他们互相之前没有任何关系 |
| 67 | +``` |
| 68 | + |
| 69 | +推荐文章中提到了另外两种实现深拷贝方法,我就直接在这敲一遍 |
| 70 | +``` |
| 71 | +function clone (source) { |
| 72 | + let target = {} |
| 73 | + for (let i in source) { |
| 74 | + if (source.hasOwnProperty(i)) { |
| 75 | + console.log(i) |
| 76 | + if( typeof source[i] === 'object'){ |
| 77 | + target[i] = clone(source[i])//注意这里,typeof 不能区分数组和对象,instanceof 可区分数组和对象 |
| 78 | + }else { |
| 79 | + target[i] = source[i] |
| 80 | + } |
| 81 | + } |
| 82 | + } |
| 83 | + return target; |
| 84 | +} |
| 85 | +let object1 = { |
| 86 | + label: '一级 3', |
| 87 | + children: [{ |
| 88 | + label: '二级 3-1', |
| 89 | + children: [{ |
| 90 | + label: '三级 3-1-1' |
| 91 | + }] |
| 92 | + }] |
| 93 | +} |
| 94 | +clone(object1) |
| 95 | +``` |
| 96 | +期待被改善的: |
| 97 | + |
| 98 | +1.没有对参数做检验 |
| 99 | + |
| 100 | +2.判断是否对象的逻辑不够严谨 |
| 101 | + |
| 102 | +3.没有考虑数组的兼容 |
| 103 | + |
| 104 | +> 生成指定深度和每层广度的代码 |
| 105 | +
|
| 106 | +这段代码的 `temp = temp['data'] = {}` 我硬是看了好久才明白,原来又是引用类型,挽狂澜于既倒,扶大厦之将倾 |
| 107 | + |
| 108 | +``` |
| 109 | +function createData(deep,breadth) { |
| 110 | + let data = {} |
| 111 | + let temp = data |
| 112 | + for (let i = 0; i < deep; i++) { |
| 113 | + temp = temp['data'] = {}//注意看后面注释 |
| 114 | + for (let j = 0; j < breadth; j++) { |
| 115 | + temp[j] = j |
| 116 | + } |
| 117 | + } |
| 118 | + return data |
| 119 | +} |
| 120 | +createData(1, 3);// 1层深度,每层有3个数据 {data: {0: 0, 1: 1, 2: 2}} |
| 121 | +let data = {} |
| 122 | +let temp = data//此时data和temp共同指向同一个地址 |
| 123 | +temp = temp['data'] = {} |
| 124 | +//temp和data指向同一个地址,temp添加了一个data属性,那么data也添加了这个属性即data=temp={data:{}} |
| 125 | +//,temp=temp['data']={},temp重新指向了一个新的地址并且和data.data指向同一个地址,只要temp发生改变,data.data 也会发生改变 |
| 126 | +``` |
0 commit comments