我是靠谱客的博主 动人唇彩,最近开发中收集的这篇文章主要介绍面试题-关于深拷贝浅拷贝,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

关于赋值

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

关于 js 变量类型

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

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

关于浅拷贝

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

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

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

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 值的拷贝,则两个变量都会同时受到影响

展开运算符

var a = {aa: 1}
var b = {...a}

深拷贝概念

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

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

实现一个深拷贝

简单版

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 的,也是不能被序列化的

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

比如:

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,手动赋值给新对象

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

上述存在以下问题:

边界条件判断

循环引用

循环引用见以下例子:

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 解决内存泄露的问题

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

最后,完整代码:

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 函数

/**
 * 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;
}

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

最后

以上就是动人唇彩为你收集整理的面试题-关于深拷贝浅拷贝的全部内容,希望文章能够帮你解决面试题-关于深拷贝浅拷贝所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部