在 JavaScript 中,因为对象和数组是引用类型,所以当我们直接将它们赋值给另一个变量时,实际上是将它们的引用地址复制了一份。这样,当我们对其中一个变量进行修改时,另一个变量也会受到影响。因此,为了避免这种情况,我们通常需要使用拷贝方法来复制一个对象或数组的值并创建一个新的副本。本文将来介绍浅拷贝和深拷贝的概念以及它们的应用场景。
备注:javascript的深浅拷贝只有引用类型会产生。
判断引用类型的方式
obj instanceof Object # 判断一个变量的父类是否为Object,是则返回true(为引用类型)
typeof(obj) # 判断一个变量的类型,为Object则为引用类型
js数据类型
最新的 ECMAScript 标准定义了 8 种数据类型:
1.七种基本数据类型:
布尔值(Boolean),有 2 个值分别是:true 和 false.
null,一个表明 null 值的特殊关键字。JavaScript 是大小写敏感的,因此 null 与 Null、NULL或变体完全不同。
undefined,和 null 一样是一个特殊的关键字,undefined 表示变量未赋值时的属性。
数字(Number),整数或浮点数,例如: 42 或者 3.14159。
字符串(String),字符串是一串表示文本值的字符序列,例如:"Howdy" 。
代表(Symbol)( 在 ECMAScript 6 中新添加的类型).。一种实例是唯一且不可改变的数据类型。
任意精度的整数 (BigInt) ,可以安全地存储和操作大整数,甚至可以超过数字的安全整数限制。
2.以及对象(Object),即引用类型。包括 Object 、Array、Function 等。
浅拷贝
浅拷贝是指将原对象或数组的值复制到一个新的对象或数组中,但是新的对象或数组的属性或元素依然是原对象或数组的引用,这意味着当我们修改其中一个对象或数组时,另一个对象或数组也会受到影响。因此,浅拷贝通常只针对引用类型。下面是常见的浅拷贝方法:
Object.create() 方法可以用于创建一个新对象,并将原对象作为新对象的原型。这样,新对象就可以访问原对象的所有属性和方法。
Object.assign() 方法是用于将一个或多个源对象的属性复制到目标对象中。它的语法如下:
Object.assign(target, ...sources)
[].concat() 方法可以用于将一个或多个数组合并成一个新数组。
[...arr]数组解构是一种将数组或类数组对象中的值提取出来,赋值给变量的方法。它可以让我们更方便地访问数组的元素。
Object.create(obj)
Object.create() 方法可以用于创建一个新对象,并将原对象作为新对象的原型。这样,新对象就可以访问原对象的所有属性和方法。
示例代码:
const obj1 = { name: '张三', age: 18 };
const obj2 = Object.create(obj1);
console.log(obj2.name); // 张三
obj1.name = '李四'
console.log(obj2.name); //李四
我们可以看到,obj2具有obj1的属性,且当obj1的属性修改时,obj2的这个属性也变了。
深拷贝
深拷贝是指将原对象或数组的值复制到一个新的对象或数组中,并且新的对象或数组的属性或元素完全独立于原对象或数组,即它们不共享引用地址。因此,当我们修改其中一个对象或数组时,另一个对象或数组不会受到任何影响。
常见的深拷贝方法是使用 JSON.parse(JSON.stringify(obj)),它的语法如下:
let newObj = JSON.parse(JSON.stringify(obj));
JSON.stringify() 方法是 JavaScript 的一个内置方法,用于将一个 JavaScript 对象或值转换为 JSON 字符串,而JSON.parse()用于将 JSON 字符串解析为对应的 JavaScript 对象或值。
这个方法可以将一个对象序列化为 JSON 字符串,然后再将 JSON 字符串解析为一个新的对象,因为它曾转化为字符串,所以不会出现像浅拷贝中那种引用类型指向同一地址的情况,从而实现深拷贝。
let obj = {
name: 'A',
age:18,
a:{
n:1
}
}
let obj2 = JSON.parse(JSON.stringify(obj))
obj.a.n = 2
console.log(obj2.a.n); //输出1
但是,这种方法也存在一些缺陷:
不能处理 undefined、function 和 Symbol 等特殊数据类型,因为它们在 JSON 中没有对应的表示方式。
无法处理循环引用,即当一个对象的属性指向自身或形成循环引用关系时,深拷贝会陷入无限递归。
手写实现深拷贝
function deepCopy(obj){
//不是引用类型就不拷贝
if(!(obj instanceof Object)) return obj
//如果形参obj是数组,就创建数组,如果是对象就创建对象
let objCopy = obj instanceof Array ? [] : {}
for(let key in obj){
if(obj instanceof Object){
objCopy[key] = deepCopy(obj[key])
} else{
if(obj.hasOwnProperty(key)){
objCopy[key] = obj[key]
}
}
}
return objCopy
}