关于赋值
赋值,即直接将变量相等,=,此时对于这两个变量来讲,指向了同个内存地址,所以,内存地址指向的内容一旦改变就会同步改变
关于 js 变量类型
基本数据类型: Number, Boolean, String, Symbol, null, undefined
引用类型: Object,诸如 array, function, object
关于浅拷贝
浅拷贝的理解就是两个变量的内存地址是不一样的,但对于不同类型的变量是有区别的:
基本数据类型: 一个变量的更改不会引起另一个变量的更改
引用类型:一个变量的内容更改会引起另一个变量的更改
1
2
3
4
5
6
7
8
9const a = [{aa: 1}] var b = a.map(item => { // 因为此时item是一个对象,所以是直接更改item.aa也会影响a item.aa = 2 return item }) console.log(b) // b: {aa: 2} console.log(a) // a: {aa: 2}
实现一个浅拷贝
Object.assign
利用 Object.assign 能进行浅拷贝,仅拷贝一级 key 的 value 值,如果你对原值进行二级以上的 value 值的拷贝,则两个变量都会同时受到影响
展开运算符
1
2
3var a = {aa: 1} var b = {...a}
深拷贝概念
浅拷贝的理解就是两个变量的内存地址是不一样的,一般更加应用于引用类型
引用类型:一个变量的内容更改不会引起另一个变量的更改,因为内容对应的内存地址不一样,所以取到的值也就不一样
实现一个深拷贝
简单版
1
2
3
4
5
6
7
8
9
10
11
12
13var obj = {a: 1} function clone(origin) { return JSON.parse(JSON.stringify(origin)) } var copy = clone(obj) copy.a = 2 console.log(obj) console.log(copy)
打印出来的结果是:
可以看到两者不受影响了
上述方法存在问题,比如
原对象有函数,是不能被序列化的
原对象有 value 是 undefined 的,也是不能被序列化的
原对象有正则的,不能被序列化
比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19var obj = { a: 1, b: undefined, say: function() { console.log(this.a) } } function clone(origin) { return JSON.parse(JSON.stringify(origin)) } var copy = clone(obj) copy.a = 2 console.log(obj) console.log(copy)
可见:第二个对象丢失了 b,say 等 key
进阶版+1
利用递归所有的 key,手动赋值给新对象
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
29var obj = { a: 1, b: undefined, c: { cc: 1 }, say: function () { console.log(this.a); }, }; function clone(origin) { let newValue = new origin.constructor(); for (let key in origin) { if (typeof origin[key] === 'object') { debugger; newValue[key] = clone(origin[key]); } else { newValue[key] = origin[key]; } } return newValue; } var copy = clone(obj); copy.c.cc = 2; console.log(obj); console.log(copy);
进阶版+2
上述存在以下问题:
边界条件判断
循环引用
循环引用见以下例子:
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
27var obj = { a: 1, }; var obj2 = { obj: obj, }; obj.obj2 = obj2; function clone(origin) { let newValue = new origin.constructor(); for (let key in origin) { if (typeof origin[key] === 'object') { newValue[key] = clone(origin[key]); } else { newValue[key] = origin[key]; } } return newValue; } var copy = clone(obj); console.log(obj); console.log(copy);
js 达到最大调用栈了,死循环了
解决思路:
利用缓存,可以将已经存在的引用直接返回。同时,可以利用 weakmap 解决内存泄露的问题
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
32var obj = { a: 1, }; var obj2 = { obj: obj, }; obj.obj2 = obj2; // stack为缓存,是一个weapMap实例 function clone(origin, stack = new WeakMap()) { let newValue = new origin.constructor(); if (stack.has(origin)) { // 如果缓存已存在,直接返回 return stack.get(origin); } stack.set(origin, newValue); // 缓存中不存在,则新增一条记录 for (let key in origin) { if (typeof origin[key] === 'object') { newValue[key] = clone(origin[key], stack); } else { newValue[key] = origin[key]; } } return newValue; } var copy = clone(obj); console.log(obj); console.log(copy);
最后,完整代码:
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
37
38
39
40
41
42var obj = { a: 1, }; var obj2 = { obj: obj, }; obj.obj2 = obj2; function clone(origin, stack = new WeakMap()) { if (!origin) { return origin; } if (obj instanceof Date) { // 增加Date类型的copy return new Date(origin); } if (obj instanceof RegExp) { // 增加正则类型的copy return new RegExp(origin); } let newValue = new origin.constructor(); if (stack.has(origin)) { return stack.get(origin); } stack.set(origin, newValue); for (let key in origin) { if (typeof origin[key] === 'object') { newValue[key] = clone(origin[key], stack); } else { newValue[key] = origin[key]; } } return newValue; } var copy = clone(obj); console.log(obj); console.log(copy);
lodash cloneDeep 关键源码分析
clone 用到的关键的 baseClone 函数
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
37
38
39
40
41
42
43
44
45/** * The base implementation of `clone` and `cloneDeep` which tracks * traversed objects. * * @private * @param {*} value The value to clone. * @param {number} bitmask The bitmask flags. * 1 - Deep clone * 2 - Flatten inherited properties * 4 - Clone symbols * @param {Function} [customizer] The function to customize cloning. * @param {string} [key] The key of `value`. * @param {Object} [object] The parent object of `value`. * @param {Object} [stack] Tracks traversed objects and their clone counterparts. * @returns {*} Returns the cloned value. */ function baseClone(value, bitmask, customizer, key, object, stack) { // Check for circular references and return its corresponding clone. // 这里同样用到缓存 stack || (stack = new Stack()); const stacked = stack.get(value); if (stacked) { return stacked; } stack.set(value, result); const props = isArr ? undefined : keysFunc(value); // 开始遍历递归 arrayEach(props || value, (subValue, key) => { if (props) { key = subValue; subValue = value[key]; } // Recursively populate clone (susceptible to call stack limits). assignValue( result, key, baseClone(subValue, bitmask, customizer, key, value, stack) ); }); return result; }
更多精彩文章可以看我的博客,如有错误,欢迎指正,共同进步
最后
以上就是动人唇彩最近收集整理的关于面试题-关于深拷贝浅拷贝的全部内容,更多相关面试题-关于深拷贝浅拷贝内容请搜索靠谱客的其他文章。
发表评论 取消回复