js-challenges
js-challenges copied to clipboard
深拷贝
const _completeDeepClone = (target, map = new WeakMap()) => {
// 基本数据类型,直接返回
if (typeof target !== 'object' || target === null) return target
// 函数 正则 日期 ES6新对象,执行构造题,返回新的对象
const constructor = target.constructor
if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(target)
// map标记每一个出现过的属性,避免循环引用
if (map.get(target)) return map.get(target)
map.set(target, true)
const cloneTarget = Array.isArray(target) ? [] : {}
for (prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = _completeDeepClone(target[prop], map)
}
}
return cloneTarget
}
你map的value存的是true?
正则...
let obj = {
a: 1,
b: 2,
c: {
c: 1,
d: 2
}
}
// 方法一
let obj1 = JSON.parse(JSON.stringify(obj))
// 测试用例
// console.log(obj1);
// obj1.b = 22
// obj1.c.c = 11
// console.log(obj);
// 方法二
function deepClone(obj) {
let objClone = Array.isArray(obj) ? [] : {}
for(let key of Object.keys(obj)) {
objClone[key] = (typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
}
return objClone
}
console.log(deepClone(obj))
export function objDeepCopy(source) {
if (typeof source === "object") {
var sourceCopy = source instanceof Array ? [] : {}
for (var item in source) {
if (!source[item]) {
sourceCopy[item] = source[item]
} else {
sourceCopy[item] =
typeof source[item] === "object"
? objDeepCopy(source[item])
: source[item]
}
}
return sourceCopy
}
return source
}
深拷贝:只是针对Object 和 Array这样的引用数据类型的。 深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象,是“值”而不是“引用”(不是分支)
浅拷贝的实现方式:
当对象object 只有一层的时候,是深拷贝 实现方式:
- Object.assign({}, obj)
- Array.prototype.concat() 或者 slice 都是浅复制
深拷贝的实现方式:
1. json.stringfiy 和 parse
let a = {
name: 'ss',
}
let b = JSON.parse(JSON.stringify(a));
缺点:
如果obj里面有时间对象,转换过后,时间将只是字符串的形式 如果obj有RegExp, Error 对象,则序列化的结果将只得到空对象 如果obj里面有函数,undefined,序列化后的结果会把函数或undefined丢失 如果obj里有NaN,Infinity 和 -Infinity,则序列化的结果会变成null JSON.stringify() 只能序列化对象的可枚举的自由属性,例如 如果obj中的对象是由构造函数生成的,则使用JSON.parse(JSON.stringify),则会丢弃对象的constructor 如果对象中存在循环引用的情况也无法正确实现深拷贝
2. 手写递归深拷贝
const deepcolne = (target, map = new WeekMap()) =>{
// 基本类型直接返回
if(typeof target !== 'object' || target === null) return target;
// 函数 正则 日期 ES6新对象, 执行构造
const constructor = target.constructor;
if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(target);
// map 标记每一个出现过的属性,避免循环引用
if(map.get(target)) return map.get(target);
map.set(target, true);
const cloneTarget = Array.isArray(target) ? [] : {};
for(const prop in target){
cloneTarget[prop] = deepclone(target[prop], map);
}
return cloneTarget;
}
3. loadash函数库
let _ = require('loadash');
_.cloneDeep();
- 手写递归
function deepClone(origin) {
const map = new WeakMap(); // 解决循环引用
const clone = (obj) => {
if (typeof obj !== 'object') {
return obj;
}
if (map.has(obj)) {
return map.get(obj);
}
let newObj = Array.isArray(obj) ? [] : {};
map.set(obj, newObj);
Object.keys(obj).forEach(key => {
newObj[key] = clone(obj[key]);
})
return newObj;
}
return clone(origin);
}
- chrome98以上支持原生深拷贝方法
const newObj = structuredClone(obj); // 不能拷贝函数
// 判断数据类型
const isType = (val) => {
return (type) => Object.prototype.toString.call(val) === `[object ${type}]`
}
function deepMerge(target) {
const ret = Array.isArray(target) ? [] : {};
for (let key in target) {
if (target.hasOwnProperty(key)) {
if (isType(target[key])('Object')) {
// 判断对象的值是不是对象,如果是对象则进一步递归
ret[key] = deepMerge(target[key])
} else if (isType(target[key])('Array')) {
// 如果是数组则进一步进行遍历数组,用一个新数组[]去添加值
ret[key] = [].concat([...deepMerge(target[key])]);
} else {
ret[key] = target[key]; // 如果是基础数据类型,则直接赋值
}
}
}
return ret;
}
解决循环引用,和拷贝不可枚举对象!!
/**
* 深拷贝会拷贝不可枚举属性,浅拷贝不会
* @param {*} target
* @param {*} map 避免循环引用
*/
function _deepClone(target, map = new Map()) {
if(typeof target !== "object" || target === null) return target;
if(map.get(target)) return map.get(target); // 避免循环引用
// 除了{}和[],new target.constructor(target)都可以重新开辟内存
if(/^(Function|RegExp|Date|Set|Map)$/.test(target.constructor.name)) {
const res = new target.constructor(target);
map.set(target, res);
return res;
}
const cloneTarget = Array.isArray(target) ? [] : {}
Object.getOwnPropertySymbols(target).forEach(item => {
cloneTarget[item] = _deepClone(target[item], map);
map.set(target, cloneTarget[item]);
})
Object.getOwnPropertyNames(target).forEach(item => {
if(!target.propertyIsEnumerable(item)) {
Object.defineProperty(target, item, Object.getOwnPropertyDescriptor(target,item));
} else {
cloneTarget[item] = _deepClone(target[item], map);
map.set(target, cloneTarget[item]);
}
})
// Object.propertyIsEnumerable() 判断是否是不可枚举的属性值
return cloneTarget;
}
const arr = [1,2,3]
const data = {
o: arr,
// a: function() {},
b: {
a: arr
}
}
Object.defineProperty(data, 'c', { enumerable: false, value: 2 }) // 默认设置为不可读不可写
const res = _deepClone(data)
console.log(res)
console.log(arr == res.b.a)
function deepCopy(obj) {
// 确定需要复制的对象类型
const type = Object.prototype.toString.call(obj).slice(8, -1);
// 根据类型进行处理
switch (type) {
case "Object":
const newObj = {};
for (let key in obj) {
newObj[key] = deepCopy(obj[key]);
}
return newObj;
case "Array":
const newArr = [];
for (let i = 0; i < obj.length; i++) {
newArr.push(deepCopy(obj[i]));
}
return newArr;
case "Date":
return new Date(obj.getTime());
case "RegExp":
return new RegExp(obj);
default:
return obj;
}
}
if(map.get(target)) return map.get(target); 注意map.get(target) 返回的是啥
function isObject(val) {
return typeof val === "object" && val !== null;
}
// 难点如下:
// 1)注意收集 Symbol 属性
// 2) 注意可能会有循环引用,所以使用 map 记录,返回一个循环的指向即可,避免持续运行爆栈
// 3)注意一些特殊对象,Function,RegExp,Date,Map,Set这些,需要去构造新的实例
// 3)区分数组还是对象, 数组会有 ['0', '1', '2', 'length'] 这些属性,构造新的对象
function deepClone(obj, hash = new WeakMap()) {
if (!isObject(obj)) return obj; // 不是对象不拷贝直接返回
if (hash.has(obj)) { // 如果哈希表中有这个对象的记录,取出并返回
return hash.get(obj);
}
// 函数 正则 日期 ES6新对象,执行构造题,返回新的对象
const constructor = obj.constructor;
if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(obj);
let target = Array.isArray(obj) ? [] : {}; // 拷贝对象还是数组,创建新的内存地址
hash.set(obj, target); // hash 表记录对象
Reflect.ownKeys(obj).forEach((item) => {
if (isObject(obj[item])) { // 子属性(包括Symbol)也是对象的话递归调用
target[item] = deepClone(obj[item], hash);
} else {
target[item] = obj[item];
}
});
return target;
}
let obj = {
a: 1,
b: 2,
c: {
c: 1,
d: 2
}
}
function deepClone(obj) {
let clone = Array.isArray(obj) ? [] : {}
for (let key of Object.keys(obj)) {
if (typeof obj[key] === 'object') {
clone[key] = deepClone(obj[key])
} else {
clone[key] = obj[key]
}
}
return clone
}
let clone = deepClone(obj)
clone.a = 1000000000
console.log(obj);
没测试过,只求面试不要我运行
/**
* 基本数据类型是默认赋值等于拷贝,可直接返回
* 如果是对象则递归
* set Map Date RegExp Function
*/
function completeDeepClone(target){
if(typeof target != 'object' || target == null)return target;
const cloneTarget = {};
const constructor = target.constructor;
if(/^Set|Map|Date|RegExp|Function$/.test(target))return new constructor(target);
for(let key in target){
cloneTarget[key] = completeDeepClone(target[key]);
}
return cloneTarget;
}
const mydeepClone = (target, map = new WeakMap()) => {
if (typeof target !== "object" || typeof target === "null") return target;
const constructor = target.constructor;
if (/^Date|Function|RegExp|Set|Map$/i.test(constructor.name)) {
return new constructor(target);
}
const cloneTarget = Array.isArray(target) ? [] : {};
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
for (let key in target) {
cloneTarget[key] = mydeepClone(target[key], map);
}
return cloneTarget;
};
const target = {
field1: 1,
field2: undefined,
field3: {
child: "child",
},
field4: [2, 4, 8],
};
target.target = target;
const t1 = mydeepClone(target);
target.field3.child = "CHILD";
console.log(t1, target);
const _completeDeepClone = (target, map = new WeakMap()) => { // 基本数据类型,直接返回 if (typeof target !== 'object' || target === null) return target // 函数 正则 日期 ES6新对象,执行构造题,返回新的对象 const constructor = target.constructor if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(target) // map标记每一个出现过的属性,避免循环引用 if (map.get(target)) return map.get(target) map.set(target, true) const cloneTarget = Array.isArray(target) ? [] : {} for (prop in target) { if (target.hasOwnProperty(prop)) { cloneTarget[prop] = _completeDeepClone(target[prop], map) } } return cloneTarget }
如果Set Map里存的是对象的话,并没有深拷贝 可以用这个样例测试
let a = {
a:1
}
let test = {
map: new Map([['key1', 'value1'], ['key2', 'value2'], [a, a]]),
set:new Set([a])
}
let result = _completeDeepClone(test)
a.a=3
console.log(result)
const obj = {
name: 'xiaohei',
bool: true,
nul: null,
undef: undefined,
show: function () {
console.log('show function');
},
num: 20,
set: new Set([1, 2, 3]),
map: new Map(),
date: new Date(),
reg: /.xml/g,
info: {
msg: 'old msg',
},
sym: Symbol('a'),
};
obj.obj = obj;
obj.map.set('mapKey', 'map-value');
obj.map.set('obj', obj);
const sym = Symbol('sym');
obj[sym] = 1;
const newObj = deepClone(obj);
function deepClone(target, hash = new WeakMap()) {
if (target === null) return null;
if (target instanceof Date) return new Date(target);
if (target instanceof RegExp) return new RegExp(target);
if (typeof target !== 'object') return target;
if (hash.get(target)) return hash.get(target);
if (target instanceof Set) {
const set = new Set();
hash.set(target, set);
target.forEach((value) => {
if (typeof value === 'object') {
set.add(deepClone(value, hash));
} else {
set.add(value);
}
});
return set;
}
if (target instanceof Map) {
const map = new Map();
hash.set(target, map);
for (const [key, value] of target) {
if (typeof value === 'object') {
map.set(key, deepClone(value, hash));
} else {
map.set(key, value);
}
}
return map;
}
const cloneObj = new target.constructor();
hash.set(target, cloneObj);
Reflect.ownKeys(target).forEach((key) => {
cloneObj[key] = deepClone(target[key], hash);
});
return cloneObj;
}
function deep(target, set = new WeakSet()) { let data = Array.isArray(target) ? [] : {}; const constructor = target.constructor; if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(target); if (set.has(target)) return target; else set.add(target); Object.keys(target).forEach((key) => { let val = target[key]; if (typeof val === "object" && val !== null) { data[key] = deep(val, set); } else { data[key] = val; } }); return data; }
const _completeDeepClone = (target, map = new WeakMap()) => { // 基本数据类型,直接返回 if (typeof target !== "object" || target === null) return target;
// 函数 正则 日期 ES6新对象,执行构造题,返回新的对象
const constructor = target.constructor;
if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(target);
// 查看是否已经被map标记(出现循环引用)
if (map.get(target)) return map.get(target);
const cloneTarget = Array.isArray(target) ? [] : {};
// map记录当前对象已经创建,值为当前对象
map.set(target, cloneTarget);
for (prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = _completeDeepClone(target[prop], map);
}
}
return cloneTarget;
};
function deepClone(value, dep = new Set(), arrMap = new Map()) {
if (Array.isArray(value)) {
if (arrMap.has(value)) {
return arrMap.get(value);
}
let newArr = Array.from(value);
arrMap.set(value, newArr);
return newArr.map((item) => {
return deepClone(item, dep, arrMap);
});
}
if (typeof value === "object" && value !== null && !dep.has(value)) {
dep.add(value);
const obj = {};
for (const key in value) {
Object.hasOwnProperty.call(value, key) &&
(obj[key] = deepClone(value[key], dep, arrMap));
}
return obj;
}
return value;
}
function clone(target,deep = true,map = new WeakMap()){
if(typeof target !== 'object' || target === null){
return target
}
else if(typeof target === 'function'){
if(!deep){
return target
}
return (new Function(`return ${target}`))()
}
if(map.has(target)){
return map.get(target)
}
else{
map.set(target,target)
}
if(!deep){
return target
}
const constructor = target.constructor;
const reg = /^(RegExp|Date|Set|Map)$/
if(reg.test(constructor.name)){
return new constructor(target)
}
if(Array.isArray(target)){
return target.map(item => clone(item,deep,map))
}
else{
const result = {};
for (const key in target) {
if (Object.prototype.hasOwnProperty.call(target, key)) {
result[key] = clone(target[key],deep,map)
}
}
return result;
}
}