数组和对象的克隆
常见的数组对象的克隆
-
数组常见的浅克隆
+ es6展开运算符 + 数组内置slice,concat方法 + for循环遍历let arr = [1, 2, 3]; //方案一:es6展开运算符 let newArr = [...arr]; console.log(newArr); //方案二:数组内置slice方法 newArr = arr.slice(); console.log(newArr); newArr = arr.concat([]); console.log(newArr); //方案三:for循环遍历 let arr1 = []; for (let i = 0; i < arr.length; i++) { arr1.push(arr[i]); } console.log(arr1); -
对象常见的浅克隆
+ es6展开运算符 + 对象内置Object.assign()方法 + for循环遍历let obj = { name: "hahah", age: 12, }; //方案一:es6展开运算符 let newObj = { ...obj }; console.log(newObj); //方案二:对象内置Object.assign()方法 newObj = Object.assign({}, obj);//处理的时候包含了原始对象中 Symbol属性 的处理 console.log(newObj); let obj1 = {}; //方案三:for循环遍历 //自己用for in遍历的时候不支持对 Symbol属性 的处理 for (const key in obj) { if (obj.hasOwnProperty(key)) { const item = obj[key]; obj1[key] = item; } } console.log(obj1); -
深克隆
-
JSON.parse/stringify「变为字符串,再变为对象,这样所有的内存会重新开辟一下」
-
转换为字符串的时候,不是所有的值都支持
-
正则变为空对象
-
BigInt 处理不了,会报错
-
属性值为 undefined 或者函数的都会消失
-
日期对象变为字符串后转换不回来了
-
ArrayBuffer
-
...
let obj2 = JSON.parse(JSON.stringify(obj)); console.log(obj2 === obj); //->false console.log(obj2.b === obj.b); //->false -
-
-
-
自己封装一个方法实现数组和对象的浅克隆和深克隆
//方法库
(function () {
let obj = {};
let str = {}.toString();
[
"Number",
"String",
"Boolean",
"null",
"undefined",
"Bigint",
"Symbol",
"Array",
"Date",
"RegExp",
"Object",
"Function",
].forEach((key) => {
obj[`[object ${key}]`] = key.toLowerCase();
});
//检测数据类型
let toType = function toType(type) {
return type === null && /^(function|object)$/i.test(typeof obj)
? obj[str.call(type)]
: typeof obj;
};
// 检测是否为函数/window
let isFunction = function isFunction(obj) {
return typeof obj === "function" && typeof obj.nodeType !== "number";
};
let isWindow = function isWindow(obj) {
return obj != null && obj === obj.window;
};
// 检测是否为数据或者类数组
let isArrayLike = function isArrayLike(obj) {
let length = !!obj && "length" in obj && obj.length,
type = toType(obj);
if (isFunction(obj) || isWindow(obj)) return false;
return (
type === "array" ||
length === 0 ||
(typeof length === "number" && length > 0 && length - 1 in obj)
);
};
//遍历数组和对象
let each = function each(obj, callback) {
callback = callback || Function.prototype;
//如果是数组
if (isArrayLike(obj)) {
for (let i = 0; i < obj.length; i++) {
let item = obj[i],
result = callback.call(item, item, i);
if (result === false) {break};
}
return obj;
}
//如果是对象(遍历symbol)
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
let item = obj[key],
result = callback.call(item, , key);
if (result === false){ break};
}
}
return obj;
};
window.toType = toType;
window.each = each;
})();
//实现数组和对象的浅克隆
function shallowClone(obj) {
//1.obj必须是数组或者对象,return的结果是克隆后的数组和对象
/*
*1.对传进来的参数进行判断(只要是数组和对象我们进行正常克隆。如果是基本数据类型,返回传进来的参数;如果是symbol,返回Object(obj);如果是bigInt,返回new obj.constructor)
*2.创建一个新数组和新对象,封装一个方法能实现数组和对象的遍历
*/
let type = toType(obj),
ctor = obj.constructor;
if(obj==null) return obj;
//如果obj是symbol,bigInt
if (/^(symbol|bigInt)$/i.test(type)) return Object(obj);
//如果obj是regexp,date
if (/^(regexp|date)$/i.test(type)) return new ctor(obj);
//对于错误对象的处理
if (/^error$/i.test(type)) return new ctor(obj.message);
//如果obj是function
if (/^function$/i.test(type)) {
// 返回新函数:新函数执行还是把原始函数执行,实现和原始函数相同的效果
return function () {
return obj.call(this, ...arguments);
};
}
if (/^(object|array)$/i.test(type)) {
//2.创建数组或者对象
let newObj = new ctor();
each(obj, (item, index) => {
newObj[index] = obj[index];
});
return newObj;
}
return obj;
}
//实现数组和对象的深克隆
function deepClone(obj, cache = new Set()) {
let type = toType(obj),
Ctor = obj.constructor;
if(obj==null)return obj
if (!/^(object|array)$/i.test(type)) return shallowClone(obj);
// 避免无限套娃
if (cache.has(obj)) return obj;
cache.add(obj);
let keys = [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)];
let newObj = new Ctor();
each(keys, (item) => {
newObj[item] = deepClone(obj[item], cache);
});
return newObj;
}
let obj = {
name: "hahah",
age: 13,
class1: {
wuwu: "haah",
age1: 14,
},
};
let newObj = deepClone(obj);
console.log(newObj);