简易版
function deepCopy(target){
/*类型检查*/
if(typeof target === 'object' && target != null){
const cloneTarge = Array.isArray(target) ? [] : {};
for(let prop in target){
if(target.hasOwnProperty(prop)){
cloneTarge[prop] = target[prop]
}
}
return cloneTarge
}else{
return target
}
}
三个问题
1。解决循环引用
let obj = {
val : 100
}
obj.target = obj
这就是循环引用
创建一个Map。记录下已经拷贝过的对象,如果说已经拷贝过,那直接返回它行了。
map和weakmap
map
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
如果你需要“键值对”的数据结构,Map 比 Object 更合适。
const m = new Map()
const o = {p:'hello world'}
m.set(o,'content')
m.get(o) //content
m.has(o) //true
m.delete(o)
m.has(o) //false
/*
size属性返回 Map 结构的成员总数
*/
m.set('1','2')
m.set('3','4')
console.log(m.size)
Map.prototype.set(key, value)
set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键
set方法返回的是当前的Map对象,因此可以采用链式写法。
m.set().set().set()
Map.prototype.get(key)
get方法读取key对应的键值,如果找不到key,返回undefined。
Map.prototype.has(key)
has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
Map.prototype.delete(key)
delete方法删除某个键,返回true。如果删除失败,返回false。
Map.prototype.clear()
clear方法清除所有成员,没有返回值。
Map.prototype.keys():返回键名的遍历器。
Map.prototype.values():返回键值的遍历器。
Map.prototype.entries():返回所有成员的遍历器。
Map.prototype.forEach():遍历 Map 的所有成员
/*keys*/
for(let key of m.keys()){
console.log(key)
/*
1
3
*/
}
/*values*/
for(let value of m.values()){
console.log(value)
/*
2
4
*/
}
/*entries*/
for(let item of m.entries()){
console.log(item[0],item[1])
/*
1 2
3 4
*/
}
/*foreach*/
for(let [key,value] of m){
console.log(key,value)
/*
1 2
3 4
*/
}
weakmap
WeakMap结构与Map结构类似,也是用于生成键值对的集合。
WeakMap与Map的区别有两点。
首先,WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
WeakMap的键名所指向的对象,不计入垃圾回收机制。
// WeakMap 也可以接受一个数组,
// 作为构造函数的参数
const k1 = [1, 2, 3];
const k2 = [4, 5, 6];
const wm2 = new WeakMap([[k1, 'foo'], [k2, 'bar']]);
/*
symbol
Symbol,表示独一无二的值
Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象
*/
var obj2 = {}
var s1 = Symbol('a')
obj2[s1] = 'hello'
/*
Object.getOwnPropertySymbols()
可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
*/
Object.getOwnPropertySymbols(obj2)
//[Symbol(a)]
/*
hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
obj.hasOwnProperty(prop)
prop
要检测的属性的 String 字符串形式表示的名称,或者 Symbol。
返回值
用来判断某个对象是否含有指定的属性的布尔值 Boolean。
使用 hasOwnProperty 作为属性名
JavaScript 并没有保护 hasOwnProperty 这个属性名,因此,当某个对象可能自有一个占用该属性名的属性时,
就需要使用外部的 hasOwnProperty 获得正确的结果:
// 也可以使用 Object 原型上的 hasOwnProperty 属性
*/
var foo = {
hasOwnProperty: function() {
return false;
},
bar: 'Here be dragons'
};
Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
利用上述知识,可以做出一个深拷贝的函数
const deepcopy2 = (target, hash = new WeakMap()) => {
//对于参数处理
if(typeof target !== 'object' || target === null)
{
return target
}
if(hash.get(target)){return hash.get(target)}
const clonetarget = Array.isArray(target) ? [] : {}
hash.set(target,clonetarget)
//针对symbol属性
const symKeys = Object.getOwnPropertySymbols(target);
if(symKeys.length){
symKeys.forEach(symKey => {
if(typeof target[symKey] === 'object' && target[symKey] !== null){
clonetarget = deepcopy2(target[symKey])
}else{
clonetarget[symKey] = target[symKey]
}
});
}
for(const i in target){
if(Object.prototype.hasOwnProperty.call(target,i)){
clonetarget[i] = typeof target[i] === 'object' && target[i] !== null ? deepcopy2(target[i],hash) : target[i]
}
}
return clonetarget
}
/*
分析
首先先判断target是不是对象,如果不是直接返回target 因为基本类型数据拷贝时互相不影响
之后设置一个weakmap,用来缓存数据,因为weakmap的键名所指向的对象不计入垃圾回收机制
判断一下hash有没有target所指向的值,因为target是键名,有就返回,可以避免重复遍历
创建一个变量,用来传进来的symbol属性做判断,获取target的所有属性名
如果属性名不为空,那么就遍历
如果对象的键值不为object或者array这样的对象,那么就直接传值
如果是object或者array这样的对象,那么就递归一次,设置clonetarget[键] = target[键],这样将target的键值赋值给clonetarget,之后返回clonetarget
递归一次之后,在将clonetarget[键] = target[键],这样如果target键值有Object或者array这样的对象的话,那么就用递归,将键值赋给clonetarget
剩下的思路也是一样的
*/