# 深拷贝和浅拷贝

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。

浅拷贝的实现方式:

  • Object.assign():需注意的是目标对象只有一层的时候,是深拷贝
  • Array.prototype.concat()
  • Array.prototype.slice()
  • let b={...a}
var alpha = ['a', 'b', 'c'];
var numeric = [1, 2, 3,{k:23}];

let arr= alpha.concat(numeric);
arr[4]=99
arr[6].k1=1000

console.log(numeric)//[1, 2, 3, {k: 23, k1: 1000}]
console.log(arr)//['a', 'b', 'c', 1, 99, 3, {k: 23, k1: 1000}]

深拷贝就是在拷贝数据的时候,将数据的所有引用结构都拷贝一份。简单的说就是,在内存中存在两个数据结构完全相同又相互独立的数据,将引用型类型进行复制,而不是只复制其引用关系。 深拷贝的实现方式:

  • 热门的函数库lodash,也有提供_.cloneDeep用来做深拷贝
  • jquery 提供一个$.extend可以用来做深拷贝
  • JSON.parse(JSON.stringify())
  • 手写递归方法

原理: 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。

# 缺点

  • 会忽略 undefined
  • 会忽略 symbol
  • 不能序列化函数,JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,不能接受函数
  • 不能解决循环引用的对象(TypeError)
let obj = {  
	a: 1,  
	b: {  
	c: 2  
	}  
};  
	
obj.b.d = obj; // 创建循环引用  

console.log(obj)
	
try {  
	let stringified = JSON.stringify(obj); // 这将抛出错误  
} catch (error) {  
	console.error(error); // TypeError: Converting circular structure to JSON  
}
let a = {
  age: undefined,
  sex: Symbol('male'),
  jobs: function() {},
  name: 'yck'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "yck"}
//忽略掉函数和 undefined symbol
  • symbol属性会丢失
  • set类型数据和map类型会被处理为{}
//定义检测数据类型的功能函数
function checkedType(target) {
  return Object.prototype.toString.call(target).slice(8, -1)
}
//实现深度克隆---对象/数组
function clone(target) {
  //判断拷贝的数据类型
  //初始化变量result 成为最终克隆的数据
  let result,
    targetType = checkedType(target)
  if (targetType === 'Object') {
    result = {}
  } else if (targetType === 'Array') {
    result = []
  } else {
    return target
  }
  //遍历目标数据
  for (let i in target) {
    //获取遍历数据结构的每一项值。
    let value = target[i]
    //判断目标结构里的每一值是否存在对象/数组
    if (checkedType(value) === 'Object' || checkedType(value) === 'Array') {
      //对象/数组里嵌套了对象/数组
      //继续遍历获取到value值
      result[i] = clone(value)
    } else {
      //获取到value值是基本的数据类型或者是函数。
      result[i] = value
    }
  }
  return result
}
  • 另一种写法
Object.getOwnPropertySymbols() // 方法返回一个给定对象自身的所有 Symbol 属性的数组。
	function deepCopy(obj){	 
		 if(obj===null) return null
		 if(typeof obj!=="object") return obj;
		 if(obj instanceof RegExp) return new RegExp(obj)
		 if(obj instanceof Date) {
			 return new Date(obj)
		 }
		 if(obj instanceof Set){
			  return new Set(obj)
		 }
		 let newobj=new obj.constructor
		 //这里的newObj 是直接取得 obj 的构造函数 , [].constructor() 是[],{}.constructor() 是 {}
		 var objectSymbols = Object.getOwnPropertySymbols(obj);
		 if(objectSymbols.length){
		 			 for(let i=0;i<objectSymbols.length;i++){
		 				  console.log(objectSymbols[i])
		 				  newobj[objectSymbols[i]]=deepCopy(obj[objectSymbols[i]]) 
		 			 }	 			
		 }
		 
		 
		 // console.log(newobj)
		 for(let key in obj){
			 console.log(key)
			 if(obj.hasOwnProperty(key)){
				 newobj[key]=deepCopy(obj[key])
			 }
		 }
		 return newobj
	}
	
	let age=Symbol();
	let sss=Symbol("k")
	let obj1={
		a:1,
		b:[2,3,4],
		c:[4,{k:1}],
		d:/\d/,
		f:function(){
			console.log(1)
		},
		g:new Set([1,2,3,4]),
		[age]:25,
		[sss]:1000
		
	}
	console.log(obj1[age])
	
	console.log(obj1)
	let obj2=deepCopy(obj1)
	console.log(obj2)

如果还要更细节的考虑,那么包装对象 Number,String,Boolean等也需要另外做处理。

//Number 包装对象
var num=new Number(1); typeof num // "object"
var newNum=deepClone(num);
//newNum -> {} 空对象

所有对象都有 valueOf 方法,valueOf 方法对于:如果存在任意原始值,它就默认将对象转换为表示它的原始值。对象是复合值,而且大多数对象无法真正表示为一个原始值, 因此默认的 valueOf()方法 简单地返回对象本身,而不是返回一个原始值。数组、函数和正则表达式简单地继承了这个默认方法,调用这些类型的实例的 valueOf() 方法只是简单返回这个对象本身。

对于原始值或者包装类:

function baseClone(base){ return
	base.valueOf();
}
//Number
var num=new Number(1);
var newNum=baseClone(num);
//newNum->1

其实对于包装类,完全可以用=号来进行克隆,其实没有深度克隆一说, 这里用 valueOf 实现,语法上比较符合规范。

对于 Date 类型: 因为 valueOf 方法,日期类定义的 valueOf()方法会返回它的一个 内部表示:1970 年 1 月 1 日以来的毫秒数.因此我们可以在 Date 的原型上定义克隆的方法:

Date.prototype.clone=function(){
	return new Date(this.valueOf());
}
var date=new Date('2010'); 
var newDate=date.clone();
// newDate-> Fri Jan 01 2010 08:00:00 GMT+0800

对于正则RegExp:

RegExp.prototype.clone =
function() { 
	var pattern =this.valueOf();
	var flags = '';
	flags += pattern.global ? 'g' : '';
	flags += pattern.ignoreCase ? 'i' : '';
	flags += pattern.multiline ? 'm' : '';
	return new RegExp(pattern.source, flags);
};
var reg=new RegExp('/111/'); var
newReg=reg.clone();
//newReg-> /\/111\//
最后更新: 4/16/2024, 8:33:42 AM