我是靠谱客的博主 动人唇彩,这篇文章主要介绍面试题-关于深拷贝浅拷贝,现在分享给大家,希望可以做个参考。

关于赋值

赋值,即直接将变量相等,=,此时对于这两个变量来讲,指向了同个内存地址,所以,内存地址指向的内容一旦改变就会同步改变

关于 js 变量类型

基本数据类型: Number, Boolean, String, Symbol, null, undefined

引用类型: Object,诸如 array, function, object

关于浅拷贝

浅拷贝的理解就是两个变量的内存地址是不一样的,但对于不同类型的变量是有区别的:

基本数据类型: 一个变量的更改不会引起另一个变量的更改

引用类型:一个变量的内容更改会引起另一个变量的更改

复制代码
1
2
3
4
5
6
7
8
9
const 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
3
var a = {aa: 1} var b = {...a}

深拷贝概念

浅拷贝的理解就是两个变量的内存地址是不一样的,一般更加应用于引用类型

引用类型:一个变量的内容更改不会引起另一个变量的更改,因为内容对应的内存地址不一样,所以取到的值也就不一样

实现一个深拷贝

简单版

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
var 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)

打印出来的结果是:

20200802104031

可以看到两者不受影响了

上述方法存在问题,比如

原对象有函数,是不能被序列化的

原对象有 value 是 undefined 的,也是不能被序列化的

原对象有正则的,不能被序列化

比如:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var 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)

20200802104453

可见:第二个对象丢失了 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
29
var 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);

20200802110803

进阶版+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
27
var 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);

20200802111136

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
32
var 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);

20200802165157

最后,完整代码:

复制代码
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
var 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; }

更多精彩文章可以看我的博客,如有错误,欢迎指正,共同进步

最后

以上就是动人唇彩最近收集整理的关于面试题-关于深拷贝浅拷贝的全部内容,更多相关面试题-关于深拷贝浅拷贝内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部