简易版

  1. function deepCopy(target){
  2. /*类型检查*/
  3. if(typeof target === 'object' && target != null){
  4. const cloneTarge = Array.isArray(target) ? [] : {};
  5. for(let prop in target){
  6. if(target.hasOwnProperty(prop)){
  7. cloneTarge[prop] = target[prop]
  8. }
  9. }
  10. return cloneTarge
  11. }else{
  12. return target
  13. }
  14. }

三个问题

1。解决循环引用

  1. let obj = {
  2. val : 100
  3. }
  4. obj.target = obj

深拷贝 - 图1
这就是循环引用
创建一个Map。记录下已经拷贝过的对象,如果说已经拷贝过,那直接返回它行了。

map和weakmap

  1. map
  2. ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
  3. 也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
  4. 如果你需要“键值对”的数据结构,Map Object 更合适。
  1. const m = new Map()
  2. const o = {p:'hello world'}
  3. m.set(o,'content')
  4. m.get(o) //content
  5. m.has(o) //true
  6. m.delete(o)
  7. m.has(o) //false
  8. /*
  9. size属性返回 Map 结构的成员总数
  10. */
  11. m.set('1','2')
  12. m.set('3','4')
  13. console.log(m.size)
  1. Map.prototype.set(key, value)
  2. set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键
  3. set方法返回的是当前的Map对象,因此可以采用链式写法。
  4. m.set().set().set()
  5. Map.prototype.get(key)
  6. get方法读取key对应的键值,如果找不到key,返回undefined
  7. Map.prototype.has(key)
  8. has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
  9. Map.prototype.delete(key)
  10. delete方法删除某个键,返回true。如果删除失败,返回false
  11. Map.prototype.clear()
  12. clear方法清除所有成员,没有返回值。
  13. Map.prototype.keys():返回键名的遍历器。
  14. Map.prototype.values():返回键值的遍历器。
  15. Map.prototype.entries():返回所有成员的遍历器。
  16. Map.prototype.forEach():遍历 Map 的所有成员
  1. /*keys*/
  2. for(let key of m.keys()){
  3. console.log(key)
  4. /*
  5. 1
  6. 3
  7. */
  8. }
  9. /*values*/
  10. for(let value of m.values()){
  11. console.log(value)
  12. /*
  13. 2
  14. 4
  15. */
  16. }
  17. /*entries*/
  18. for(let item of m.entries()){
  19. console.log(item[0],item[1])
  20. /*
  21. 1 2
  22. 3 4
  23. */
  24. }
  25. /*foreach*/
  26. for(let [key,value] of m){
  27. console.log(key,value)
  28. /*
  29. 1 2
  30. 3 4
  31. */
  32. }
  1. weakmap
  2. WeakMap结构与Map结构类似,也是用于生成键值对的集合。
  3. WeakMapMap的区别有两点。
  4. 首先,WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
  5. WeakMap的键名所指向的对象,不计入垃圾回收机制。
  1. // WeakMap 也可以接受一个数组,
  2. // 作为构造函数的参数
  3. const k1 = [1, 2, 3];
  4. const k2 = [4, 5, 6];
  5. const wm2 = new WeakMap([[k1, 'foo'], [k2, 'bar']]);
  1. /*
  2. symbol
  3. Symbol,表示独一无二的值
  4. Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象
  5. */
  6. var obj2 = {}
  7. var s1 = Symbol('a')
  8. obj2[s1] = 'hello'
  9. /*
  10. Object.getOwnPropertySymbols()
  11. 可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
  12. */
  13. Object.getOwnPropertySymbols(obj2)
  14. //[Symbol(a)]
  15. /*
  16. hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
  17. obj.hasOwnProperty(prop)
  18. prop
  19. 要检测的属性的 String 字符串形式表示的名称,或者 Symbol。
  20. 返回值
  21. 用来判断某个对象是否含有指定的属性的布尔值 Boolean。
  22. 使用 hasOwnProperty 作为属性名
  23. JavaScript 并没有保护 hasOwnProperty 这个属性名,因此,当某个对象可能自有一个占用该属性名的属性时,
  24. 就需要使用外部的 hasOwnProperty 获得正确的结果:
  25. // 也可以使用 Object 原型上的 hasOwnProperty 属性
  26. */
  27. var foo = {
  28. hasOwnProperty: function() {
  29. return false;
  30. },
  31. bar: 'Here be dragons'
  32. };
  33. Object.prototype.hasOwnProperty.call(foo, 'bar'); // true

利用上述知识,可以做出一个深拷贝的函数

  1. const deepcopy2 = (target, hash = new WeakMap()) => {
  2. //对于参数处理
  3. if(typeof target !== 'object' || target === null)
  4. {
  5. return target
  6. }
  7. if(hash.get(target)){return hash.get(target)}
  8. const clonetarget = Array.isArray(target) ? [] : {}
  9. hash.set(target,clonetarget)
  10. //针对symbol属性
  11. const symKeys = Object.getOwnPropertySymbols(target);
  12. if(symKeys.length){
  13. symKeys.forEach(symKey => {
  14. if(typeof target[symKey] === 'object' && target[symKey] !== null){
  15. clonetarget = deepcopy2(target[symKey])
  16. }else{
  17. clonetarget[symKey] = target[symKey]
  18. }
  19. });
  20. }
  21. for(const i in target){
  22. if(Object.prototype.hasOwnProperty.call(target,i)){
  23. clonetarget[i] = typeof target[i] === 'object' && target[i] !== null ? deepcopy2(target[i],hash) : target[i]
  24. }
  25. }
  26. return clonetarget
  27. }
  1. /*
  2. 分析
  3. 首先先判断target是不是对象,如果不是直接返回target 因为基本类型数据拷贝时互相不影响
  4. 之后设置一个weakmap,用来缓存数据,因为weakmap的键名所指向的对象不计入垃圾回收机制
  5. 判断一下hash有没有target所指向的值,因为target是键名,有就返回,可以避免重复遍历
  6. 创建一个变量,用来传进来的symbol属性做判断,获取target的所有属性名
  7. 如果属性名不为空,那么就遍历
  8. 如果对象的键值不为object或者array这样的对象,那么就直接传值
  9. 如果是object或者array这样的对象,那么就递归一次,设置clonetarget[键] = target[键],这样将target的键值赋值给clonetarget,之后返回clonetarget
  10. 递归一次之后,在将clonetarget[键] = target[键],这样如果target键值有Object或者array这样的对象的话,那么就用递归,将键值赋给clonetarget
  11. 剩下的思路也是一样的
  12. */