Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6.深拷贝实现 #7

Open
conan1992 opened this issue May 30, 2020 · 0 comments
Open

6.深拷贝实现 #7

conan1992 opened this issue May 30, 2020 · 0 comments

Comments

@conan1992
Copy link
Owner

conan1992 commented May 30, 2020

1.简单实现

深拷贝拆分之后是浅拷贝+递归;浅拷贝时判断属性值是否为对象,如果是对象就递归再进行拷贝;

function deepCopy(data){
           var result = {}
           for(var attr in data){
               if(typeof data[attr] === 'object'){
                   
                    result[attr] = deepCopy(data[attr]);
               }else{
                    result[attr] = data[attr];
               }
                
           }
           return result
 }
 Object.prototype.father ="I am your father"
var a = { a: 1, b: 2, c: {d: 1}}
var sym1 = Symbol("a");
a[sym1] = 'sym1'
var b = deepCopy(a);
a.c.d = 2
console.log(a, b)

image
打印结果分析:

  • 发现新对象多了father属性,这是因为for...in 循环只遍历可枚举属性(包括它的原型链上的可枚举属性)
  • for in无法遍历Symbol属性;
  • 不兼容数组
  • 没有对传入参数进行校验,传入 null 时应该返回 null 而不是 {}
  • 对于对象的判断逻辑不严谨,因为 typeof null === 'object'

2.改进(兼容数组,过滤原型链上的数据)

  • 兼容数组:
typeof null //"object"
typeof {} //"object"
typeof [] //"object"
typeof function foo(){} //"function" (特殊情况)

//封装 
function isObject(data){
    return typeof data === 'object' && data !== null
}
  • 过滤原型链上的数据:
Object.prototype.hasOwnProperty
function deepCopy01(data){
    if(!isObject(data)) return data;
    var result = Array.isArray(data) ? [] : {};
    for(var attr in data){
        if(Object.prototype.hasOwnProperty.call(data,attr)){
            if(isObject(data[attr])){
                        result[attr] = deepCopy01(data[attr]);
            }else{
                    result[attr] = data[attr];
             }
         }
      }
       return result
}
Object.prototype.father ="I am your father"
var a = { a: [1,2,3], b: 2, c: {d: 1}}
var sym1 = Symbol("a");
a[sym1] = 'sym1'
var b = deepCopy01(a);
 a.c.d = 2
console.log(a, b)

image

3.改进(拷贝Symbol)

  • 获取symbol属性
Object.getOwnPropertySymbols(...)
//获取到 ?symbol 类型的数组

function deepCopy02(data){
	if(!isObject(data)) return data;
	var result = Array.isArray(data) ? [] : {};

	//获取symbol属性
	var syms = Object.getOwnPropertySymbols( data )
	if(syms.length){
		syms.forEach(function(item){
			if(isObject(data[item])){
				result[item] = deepCopy02(data[item]);
			}else{
				result[item] = data[item];
			}
		})
	}

   for(var attr in data){
	   if(Object.prototype.hasOwnProperty.call(data,attr)){
			if(isObject(data[attr])){
				result[attr] = deepCopy02(data[attr]);
			}else{
					result[attr] = data[attr];
			}
	   }
	}
	return result
}
Object.prototype.father ="I am your father"
var a = { a: [1,2,3], b: 2, c: {d: 1}}
var sym1 = Symbol("a");
a[sym1] = 'sym1'
var b = deepCopy02(a);
a.c.d = 2
console.log(a, b)

image

4.循环引用

var a = {
	name: "manny",
} 
a.a = a
console.log(deepCopy02(a))
//Uncaught RangeError: Maximum call stack size exceeded
//循环引用导致爆栈

//并且
JSON.parse(JSON.stringify(a));
// TypeError: Converting circular structure to JSON
  • 方法一
function deepCopy03(data, hash = new WeakMap()){
	if(!isObject(data)) return data;
	if(hash.has(data)) return hash.get(data);
	var result = Array.isArray(data) ? [] : {};
	hash.set(data, result)
	//获取symbol属性
	var syms = Object.getOwnPropertySymbols( data )
	if(syms.length){
		syms.forEach(function(item){
			if(isObject(data[item])){
				result[item] = deepCopy03(data[item], hash);
			}else{
				result[item] = data[item];
			}
		})
	}

   for(var attr in data){
	   if(Object.prototype.hasOwnProperty.call(data,attr)){
			if(isObject(data[attr])){
				result[attr] = deepCopy03(data[attr], hash);
			}else{
				result[attr] = data[attr];
			}
	   }
	}
   return result
}
var a = {
	name: "manny",
}
var sym = Symbol("b");
a.a = a;
a[sym] = a;
console.log(deepCopy03(a))
  • 打印结果
    image
  • 方法2-使用数组
    这里用了es6的 WeakMap,那么我们用数组来兼容
function isObject(data){
	return typeof data === 'object' && data !== null
}
function arrayHasItem(array, target){
	for(var i=0;i<array.length;i++){
		if(array[i].source === target){
			return array[i].target
		}
	}
	return null
}
function deepCopy04(data, array){
	array = array || [];
	if(!isObject(data)) return data;
	if( arrayHasItem(array, data) ) return arrayHasItem(array, data);
	var result = Array.isArray(data) ? [] : {};
	
	array.push({
		source: data,
		target: result
	})
	//获取symbol属性
	var syms = Object.getOwnPropertySymbols( data )
	if(syms.length){
		syms.forEach(function(item){
			if(isObject(data[item])){
				result[item] = deepCopy04(data[item], array);
			}else{
				result[item] = data[item];
			}
		})
	}

   for(var attr in data){
	   if(Object.prototype.hasOwnProperty.call(data,attr)){
			if(isObject(data[attr])){
				result[attr] = deepCopy04(data[attr], array);
			}else{
				result[attr] = data[attr];
			}
	   }
	}
   return result
}
var a = {
    name: "manny",
}
var sym = Symbol("b");
a.a = a;
a[sym] = a;
var b = deepCopy04(a);
a.name = "mo"
console.log(a, b)

5.引用丢失

var obj1 = {};
var obj2 = {a: obj1, b: obj1};

obj2.a === obj2.b; 
// true

var obj3 = deepCopy02(obj2);
obj3.a === obj3.b; 
// false

引用丢失在某些情况下是有问题的,比如上面的对象 obj2,obj2 的键值 a 和 b 同时引用了同一个对象 obj1,使用 deepCopy02进行深拷贝后就丢失了引用关系变成了两个不同的对象,deepCopy03和deepCopy04再解决循环引用的同时,已经解决了丢失的情况;(同一个对象被存储重复利用)

6.递归爆栈

递归的存在,会存在爆栈的情况;

function createData(deep, breadth){
    var result = {}
    var temp = result;
    for(var i=0;i<deep;i++){
        temp = temp.data = {};
        for (var j = 0; j < breadth; j++) {
            temp[j] = j;
        }
    }
    return result
}
var a = createData(10000,0)
console.log( deepCopy04(a) )
//Uncaught RangeError: Maximum call stack size exceeded

function deepCopy05(data){
	var array = [];//存储数据;
	if(!isObject(data)) return data;
	//if( arrayHasItem(array, data) ) return arrayHasItem(array, data);
	var result = Array.isArray(data) ? [] : {};
	var loopList = [{
		parent: result,
		key: undefined,
		value: data
	}]
	
	while(loopList.length){
		var node = loopList.pop();
		var parent = node.parent;
		var key = node.key;
		var value = node.value;
		var res = parent;

		if(typeof key != 'undefined'){
			res = parent[key] =  Array.isArray(value) ? [] : {};
		}
		if( arrayHasItem(array, value) ){
			parent[key] = arrayHasItem(array, value);
			continue
		}
		array.push({
			source: value,
			target: res
		})
		//获取symbol属性
		var syms = Object.getOwnPropertySymbols( value )
		if(syms.length){
			syms.forEach(function(item){
				if(isObject(value[item])){
					loopList.push({
						parent: res,
						key: item,
						value: value[item]
					})
				}else{
					res[item] = value[item];
				}
			})
		}

		for(var attr in value){
			if(Object.prototype.hasOwnProperty.call(value,attr)){
					if(isObject(value[attr])){
						loopList.push({
							parent: res,
							key: attr,
							value: value[attr]
						})
					}else{
						res[attr] = value[attr];
					}
			}
		}
	}
	
   return result
}
var a = createData(10000,1)
var b = deepCopy05(a)

console.log( a, b )

参考

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant