fe-interview
fe-interview copied to clipboard
[js] 第44天 深度克隆对象的方法有哪些,并把你认为最好的写出来
第44天 深度克隆对象的方法有哪些,并把你认为最好的写出来
我比较喜欢使用原生的方法,足够简单,而且可以解决大多数的深拷贝。
var obj1={
name: '张三',
age:14,
friend:['小红','小白'],
parents:{
mother:{name: '李岚',age:34},
father:{name: '张武',age:35}
}
}
var obj2 = JSON.parse(JSON.stringify(obj1))
obj1.mother === obj2.parents.mother//false
我比较喜欢使用原生的方法,足够简单,而且可以解决大多数的深拷贝。
var obj1={ name: '张三', age:14, friend:['小红','小白'], parents:{ mother:{name: '李岚',age:34}, father:{name: '张武',age:35} } } var obj2 = JSON.parse(JSON.stringify(obj1)) obj1.mother === obj2.parents.mother//false
是挺好的, 就是容易出bug
递归调用拷贝。json.parse可以解决一般的对象深拷贝,但是函数类型的对象就不行了
全面的deep clone最坑爹的一点就是对象内部引用自己
- 容易理解是 JSON.parse JSON.stringify
- 递归
// 深度遍历
// 广度遍历
数组: newArr=[...oldArr] 只包含数据的对象: JSON.parse 复杂对象:递归,解构
数组: newArr=[...oldArr] 只包含数据的对象: JSON.parse 复杂对象:递归,解构
对一维数组,才能使用newArr=[...oldArr]
数组clone:Array.from( ) 对象clone:JSON.parse(JSON.stringify( ))
function deepClone(obj) {
if (Object.prototype.toString.call(obj).slice(8, -1) === 'Object') {
var newObj = {}
for (const key in obj) {
newObj[key]=deepClone(obj[key])
}
return newObj
} else if (Object.prototype.toString.call(obj).slice(8, -1) === 'Array') {
var newArr = []
for (const index in obj) {
newArr[index]=deepClone(obj[index])
}
return newArr
}
return obj
}
const isType = type => target =>
Object.prototype.toString.call(target) === `[object ${type}]`
const isArray = isType('Array')
const isObject = isType('Object')
function extend(target, source, deep) {
for (const key in source)
if (source.hasOwnProperty(key))
if (deep && (isArray(source[key]) || isObject(source[key]))) {
if (isArray(source[key]) && !isArray(target[key])) target[key] = []
if (isObject(source[key]) && !isObject(target[key])) target[key] = {}
extend(target[key], source[key], deep)
} else if (source[key] !== undefined) target[key] = source[key]
}
function clone(target) {
let deep,
args = [].slice.call(arguments, 1)
if (typeof target === 'boolean') {
deep = target
target = args.shift()
}
args.forEach(source => extend(target, source, deep))
return target
}
我习惯用json.parser(json.stringify()), 麻烦点的是自己写个方法把对象遍历出来赋值
数组: newArr=[...oldArr] 只包含数据的对象: JSON.parse 复杂对象:递归,解构
这个小老弟, 扩展运算符那个是浅拷贝,深拷贝最简单的是 JSON.parse JSON.stringify
但对于 undefined function symbol 是会忽略的。
你API层面得到的都是浅拷贝。
通常来说,都是递归来实现一个 deepClone
function deepCopy(newObj, oldObj) {
for (var key in oldObj) {
var item = oldObj[key]
if (item instanceof Array) {
newObj[key] = [];
deepCopy(newObj[key], item);
}
else if (item instanceof Object) {
newObj[key] = {};
deepCopy(newObj[key], item);
}
else {
newObj[key] = item;
}
}
return newObj;
}
function deepCopy(source, target) {
const c = target || {}
for (const item in source) {
if (typeof source[item] === 'object') {
c[item] = (source[item].constructor === Array) ? [] : {}
deepCopy(source[item], c[item])
} else {
c[item] = source[item]
}
}
return c
}
function deepclone(t) {
let _t = t instanceof Array ? [] : {}
for (const key in t) {
_t[key] = typeof t[key] === 'object' ? deepclone(t[key]) : t[key]
}
return _t
}
递归实现
function cloneDeep(source) {
var temp = source.constructor == Array ? [] : {};
for (var keys in source) {
if (source.hasOwnProperty(keys)) {
if (source[keys] && typeof source[keys] === 'object') {
temp[keys] = source[keys].construct === Array ? [] : {};
temp[keys] = cloneDeep(source[keys]);
} else {
temp[keys] = source[keys]
}
}
}
return temp;
}
const deepCopy = obj => { if (typeof obj === "object") { let newObj; if (Array.isArray(obj)) newObj = []; else newObj = {}; for (const key in obj) { newObj[key] = deepCopy(obj[key]); } return newObj; } return obj; };
deepClone = (element) => {
if(!(typeof element === 'object')) return element;
if(element === null) return null;
return element instanceof Array
? element.map(item => deepClone(item))
: Object.entries(element)
.reduce((pre,[key,val])=> ({...pre, [key]: deepClone(val)}), {});
}
//
const test1 = [1,[2],[[3]],{a:1},{b:{c:1}}];
const test2 = {a: [1], b: {c: 2}};
console.log(test1, deepClone(test1));
console.log(test2, deepClone(test2));
function deepClone(obj){
if(typeof obj !== 'object'){
return new Error('Error arguments')
}
let target = Array.isArray(obj) ? [] : {}
for(let key in obj){
if(obj && obj.hasOwnProperty(key)){
if(typeof obj[key] === 'object'){
target[key] = deepClone(obj[key])
}else{
target[key] = obj[key]
}
}
}
return target
}
简单的拷贝
递归拷贝 ==>解决了循环引用
function _clone(target, map = new Map()) {
if (typeof target !== 'object') {
return target;
}
let clonetarget = target instanceof Array ? [] : {}
if (map.has(target)) {
return map.get(target);
}
map.set(target, clonetarget);
for (const key in target) {
if (Object.prototype.hasOwnProperty.call(target, key)) {
clonetarget[key] = typeof target[key] === 'object' ? _clone(target[key], map) : target[key];
}
}
return clonetarget;
}
完整点的
const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
const argsTag = '[object Arguments]';
const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const numberTag = '[object Number]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const errorTag = '[object Error]';
const regexpTag = '[object RegExp]';
const funcTag = '[object Function]';
const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];
function forEach(array, iteratee) {
let index = -1;
const length = array.length;
while (++index < length) {
iteratee(array[index], index);
}
return array;
}
function isObject(target) {
const type = typeof target;
return target !== null && (type === 'object' || type === 'function');
}
function getType(target) {
return Object.prototype.toString.call(target);
}
function getInit(target) {
const Ctor = target.constructor;
return new Ctor();
}
function cloneSymbol(targe) {
return Object(Symbol.prototype.valueOf.call(targe));
}
function cloneReg(targe) {
const reFlags = /\w*$/;
const result = new targe.constructor(targe.source, reFlags.exec(targe));
result.lastIndex = targe.lastIndex;
return result;
}
function cloneFunction(func) {
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
const funcString = func.toString();
if (func.prototype) {
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if (body) {
if (param) {
const paramArr = param[0].split(',');
return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
} else {
return null;
}
} else {
return eval(funcString);
}
}
function cloneOtherType(targe, type) {
const Ctor = targe.constructor;
switch (type) {
case boolTag:
case numberTag:
case stringTag:
case errorTag:
case dateTag:
return new Ctor(targe);
case regexpTag:
return cloneReg(targe);
case symbolTag:
return cloneSymbol(targe);
case funcTag:
return cloneFunction(targe);
default:
return null;
}
}
function clone(target, map = new WeakMap()) {
// 克隆原始类型
if (!isObject(target)) {
return target;
}
// 初始化
const type = getType(target);
let cloneTarget;
if (deepTag.includes(type)) {
cloneTarget = getInit(target, type);
} else {
return cloneOtherType(target, type);
}
// 防止循环引用
if (map.get(target)) {
return target;
}
map.set(target, cloneTarget);
// 克隆set
if (type === setTag) {
target.forEach(value => {
cloneTarget.add(clone(value));
});
return cloneTarget;
}
// 克隆map
if (type === mapTag) {
target.forEach((value, key) => {
cloneTarget.set(key, clone(value));
});
return cloneTarget;
}
// 克隆对象和数组
const keys = type === arrayTag ? undefined : Object.keys(target);
forEach(keys || target, (value, key) => {
if (keys) {
key = value;
}
cloneTarget[key] = clone(target[key], map);
});
return cloneTarget;
}
function deepCopy (obj) {
var result;
//引用类型分数组和对象分别递归
if (Object.prototype.toString.call(obj) == '[object Array]') {
result = []
for (i = 0; i < obj.length; i++) {
result[i] = deepCopy(obj[i])
}
} else if (Object.prototype.toString.call(obj) == '[object Object]') {
result = {}
for (var attr in obj) {
result[attr] = deepCopy(obj[attr])
}
}
//值类型直接返回
else {
return obj
}
return result
}
我比较喜欢使用原生的方法,足够简单,而且可以解决大多数的深拷贝。
var obj1={ name: '张三', age:14, friend:['小红','小白'], parents:{ mother:{name: '李岚',age:34}, father:{name: '张武',age:35} } } var obj2 = JSON.parse(JSON.stringify(obj1)) obj1.mother === obj2.parents.mother//false
@shufangyi
const isType = type => target => Object.prototype.toString.call(target) === `[object ${type}]` const isArray = isType('Array') const isObject = isType('Object') function extend(target, source, deep) { for (const key in source) if (source.hasOwnProperty(key)) if (deep && (isArray(source[key]) || isObject(source[key]))) { if (isArray(source[key]) && !isArray(target[key])) target[key] = [] if (isObject(source[key]) && !isObject(target[key])) target[key] = {} extend(target[key], source[key], deep) } else if (source[key] !== undefined) target[key] = source[key] } function clone(target) { let deep, args = [].slice.call(arguments, 1) if (typeof target === 'boolean') { deep = target target = args.shift() } args.forEach(source => extend(target, source, deep)) return target }
学习了。
/** 将目标对象克隆到一个新对象
* @param target {Object.Array} 克隆到的最新对象或数组
* @param source {Object.Array} 被克隆的原始对象或数组。它要与 target 的类型保持一致。
* @param deep {Boolean} 深克隆的标识。true: 深克隆; false(默认): 浅克隆。
* @return void
*/
extend(target, source, deep)
`
function deepclone(obj) {
if (Object.prototype.toString.call(obj).slice(8, -1) === 'Object') {
const newObj = {};
for (const key in obj) {
newObj[key] = deepclone(obj[key])
}
return newObj;
} else if (Object.prototype.toString.call(obj).slice(8, -1) === 'Array') {
const newArr = [];
for (const index in obj) {
newArr[index] = deepclone(obj[index]);
}
return newArr;
}
return obj;
}
let arr = [1, 2, 3, [1, 2, 3], 5];
let obj = {
name: 'zhangsan',
course: ['JS', 'CSS', 'HTML']
}
let newarr = deepclone(arr);
arr[3][1] = 9;
console.log(newarr);
let newobj = deepclone(obj);
obj.course[1] = 'Vue';
console.log(newobj);
//2.JSON.parse(JSON.stringify())
//弊端:undefined、function、symbol会在转换过程中被忽略
//3.第三方插件:比如lodash的深拷贝
`
function deepClone(target){ let result if (res.isArray(target) === 'Array') { result = [] for(let o in target) { result.push(deepClone(target[o])) } }else if(res.isObject(target) === 'Object') { result = {} for(let o in target) { result[o] = deepClone(target[o]) } } else { // 基本数据类型 result = target } return result }
最好的感觉还是Json.stringify(obj)
function deepClone(obj){
if(!obj instanceof Object ) return;
let target = Array.isArray(obj) ? [] : {};
for(let key in obj){
if(obj.hasOwnPrototype(key)){
if(type obj[key] === 'object'){
target[key] = deepClone(obj[key]);
}else{
target[key] = obj[key];
}
}
}
return target;
}