赋值引用
let arr = [1, 2, 3];
let newArr = arr;
newArr[0] = 100;
console.log(arr);//[100, 2, 3]
浅拷贝
- 它只能拷贝一层对象。如果有对象的嵌套,那么嵌套的内容还是指向同一块内存
自定义实现:
const shallowClone = (target) => {
if (typeof target === 'object' && target !== null) {
const cloneTarget = Array.isArray(target) ? []: {};
for (let prop in target) {
//虽然返回的是新对象,但是嵌套的对象是直接拿的引用
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = target[prop];
}
}
return cloneTarget;
} else {
return target;
}
}
方式一:
let target2=Object.assign(target,source,...)
方式二:
let arr = [1, 2, {val: 4}];
let newArr = arr.slice();
newArr[2].val = 1000;
console.log(arr);//[ 1, 2, { val: 1000 } ]
方式三:
let arr = [1, 2, 3];
let newArr = arr.concat();
newArr[1] = 100;
console.log(arr);//[ 1, 2, 3 ]
方式四:
let arr = [1, 2, 3];
let newArr = [...arr];//跟arr.slice()是一样的效果
深拷贝
- 创建一个新的空对象,开辟一块内存,然后将原对象中的数据全部复制过去,完全切断两个对象间的联系。
方式一:
const deepClone = (target) => {
if (typeof target === 'object' && target !== null) {
const cloneTarget = Array.isArray(target) ? []: {};
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = deepClone(target[prop]);
}
}
return cloneTarget;
} else {
return target;
}
}
JSON 方式深拷贝
- Date 转换为字符串
- Set、Map、Regex、File、Error 转换为 {}
let o=JSON.stringify(target);
let deep=JSON.parse(o);
递归深拷贝详细实现
- 上面的深拷贝无法解决循环引用的问题
let obj = {val : 100};
obj.target = obj;
deepClone(obj);//报错: RangeError: Maximum call stack size exceeded
-
解决循环引用
- 创建一个Map。记录下已经拷贝过的对象,如果说已经拷贝过,那直接返回
- 循环引用造成报错的原因是无限递归造成的爆栈,所以需要直接返回对象,避免报错,但循环引用的问题并没有解决,对象依旧是无限引用的
const isObject = (target) => (typeof target === 'object' || typeof target === 'function') && target !== null;
const deepClone = (target, map = new Map()) => {
if(map.get(target))
return target;
if (isObject(target)) {
map.set(target, true);
const cloneTarget = Array.isArray(target) ? []: {};
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = deepClone(target[prop],map);
}
}
return cloneTarget;
} else {
return target;
}
}
- vuex中解决循环引用
function find (list, f) {
return list.filter(f)[0]
}
/**
* Deep copy the given object considering circular structure.
* This function caches all nested objects and its copies.
* If it detects circular structure, use cached copy to avoid infinite loop.
*
* @param {*} obj
* @param {Array<Object>} cache
* @return {*}
*/
function deepCopy (obj, cache = []) {
// just return if obj is immutable value
if (obj === null || typeof obj !== 'object') {
return obj
}
// if obj is hit, it is in circular structure
const hit = find(cache, c => c.original === obj)
if (hit) {
return hit.copy
}
const copy = Array.isArray(obj) ? [] : {}
// put the copy into cache at first
// because we want to refer it in recursive deepCopy
cache.push({
original: obj,
copy
})
Object.keys(obj).forEach(key => {
copy[key] = deepCopy(obj[key], cache)
})
return copy
}
结构化克隆
- 克隆循环引用
- 克隆各种 JavaScript 类型,比如 Date 、 Set 、 Map 、 Error 、 RegExp 、 ArrayBuffer 、 Blob 、 File 、 ImageData 等等
- 传送可转移对象(transferable objects)
- 无法克隆 Symbol 类型
- 无法克隆函数(报错处理)
- 无法克隆 DOM 节点
- 属性描述符 setters 和 getters
structuredClone({
get foo() {
return 'bar'
}
})
// 结果变成: { foo: 'bar' }
- 原型链不会被克隆,且克隆对象将不再被视为此类的实例
class MyClass {
foo = 'bar'
myMethod() {
/* ... */
}
}
const myClass = new MyClass()
const cloned = structuredClone(myClass) // 结果变成: { foo: 'bar' }
cloned instanceof myClass // false