拷贝
js的数据结构是可变的,而Js里面拷贝有这么多类型的根本原因是内存,而需求不同也会导致有时候不需要节省那么多内存,需要不变增加内存来换稳定性。
对于拷贝,这里主要说的是拷贝对象,基本数据类型就是直接在内存里开个新空间赋值进去了,新空间和旧空间是不一样的两个空间,而拷贝对象有四种情况,一种是基础类型拷贝,一种是赋值,一种是浅拷贝,一种是深拷贝,而深拷贝又可以分为JSON深拷贝,和递归深拷贝。
基础类型拷贝是在栈内新建一个独立小房间,两个小房间完全无关。
对象在栈里存的是堆里的内存地址,赋值则是复制了内存地址这个钥匙,于是栈里存在了两个一模一样的钥匙,如果我想修改对象的家装,新钥匙开门见到的也会是新的家装。
而浅拷贝的对象是一个小区,有公共区域(引用类型),和小区房间(基础类型),浅拷贝时我给了住户一模一样的栈公共区域钥匙(堆内引用类型内存地址),为新住户在栈内建好了一模一样的房间(栈内复制基础类型值),所有租客都能修改公共区域的家装,互相影响,但所有租客修改自己房间家装时,大家是互不知道的。而浅拷贝发生时,新住户则拥有了房间(基础类型值)和公共区域钥匙(引用类型地址),栈内增加了新房间,堆内公共区域不变。
深拷贝则是新建一模一样的别墅,每栋别墅内的家装(引用类型)和户外花园(引用类型)一开始都是一样的,但一旦拷贝后进行修改家装和花园,就互不干涉,互相不知道了。此时栈和堆都被占了新内存。
| 拷贝方式 | 第一层 | 第二层+ | 函数 | 循环引用 |
|---|---|---|---|---|
| 赋值 | 地址 | 地址 | 保留 | 安全 |
| 浅拷贝 | 值 | 地址 | 保留 | 安全 |
| JSON深拷贝 | 值 | 值 | 丢失 | 报错 |
| 递归深拷贝 | 值 | 值 | 保留 | 需处理 |
| 方面 | 赋值 | 浅拷贝 | 深拷贝 |
|---|---|---|---|
| 内存地址 | 完全共享 | 对象本身地址不同 引用属性地址相同 |
完全新建 |
| 基础类型 | 共享修改 | 独立修改 | 独立修改 |
| 引用类型 | 共享修改 | 共享修改 | 独立修改 |
| 内存占用 | 最小 | 中等 | 最大 |
| 性能 | 最快 | 较快 | 较慢 |
// 场景1:需要共享状态 - 用赋值
let globalConfig = { theme: "dark", language: "zh" };
let headerConfig = globalConfig; // 共享配置
let footerConfig = globalConfig;
// 场景2:需要部分独立 - 用浅拷贝
let userTemplate = { name: "", settings: { theme: "light" } };
let user1 = { ...userTemplate, name: "张三" }; // 独立名字,共享设置
let user2 = { ...userTemplate, name: "李四" };
// 场景3:需要完全独立 - 用深拷贝
let criticalData = { sensitive: true, records: [1, 2, 3] };
let backup = JSON.parse(JSON.stringify(criticalData)); // 完全独立备份
方式
赋值
全部拷贝地址。
浅拷贝
第一层拷贝值,深层拷贝地址
- - 扩展运算符(...)
- Object.assign()
- Array.prototype.slice()
- Array.prototype.concat()
- Array.from()
深拷贝
全部拷贝值。
- JSON.stringify() + JSON.parse()
- structuredClone()
扩展运算符(...)
扩展运算符,可以在函数调用/数组构造时,将数组表达式或者 string 在语法层面展开;还可以在构造字面量对象时,将对象表达式按 key-value 的方式展开
适用对象:Object/Array
用于取出参数对象里的所有可遍历属性,拷贝到当前对象中。
let obj = {a:1,b:2}
let obj2 = {...obj}
//等于
let obj = {a:1,b:2}
let obj2 = Object.assign({},obj)
Object.assign
Object.assign()方法用于将一个或多个源对象的属性复制到目标对象中。
它拷贝的是对象的属性的引用,而不是对象本身.
语法:
Object.assign(_target_, _source(s)_)
| 参数 | 描述 |
|---|---|
| target | 必需。目标对象。 |
| source | 必需。一个或多个源对象(被复制对象) |
const dog = {
sound : 'Wof',
walkDay: ['Mon','Wed',['morning']],
food : function(){
console.log(`${this.sound}!Need some meat!`);
}
}
const dog2 = Object.assign({},dog)
dog2.sound = 'changed';
console.log(dog.sound); //Wof——证明是浅拷贝
console.log(dog2.food()); //Wof!Need some meat!
针对数组的浅拷贝
slice
slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin不包括 end )。原始数组不会被改变
适用对象:Array
concat
concat() 方法用于合并两个或多个数组。此方法不会改变现有数组,而是返回一个新数组
适用对象:Array