深拷贝

参考

  • 浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
  • 深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

    1. const deepClone = (obj = {}) => {
    2. if (typeof obj !== 'object' || obj == null ) {
    3. return obj
    4. }
    5. // 或者 Array.isArray(target) ? [] : {};
    6. let result = obj instanceof Array ? [] : {}
    7. for (let key in obj) {
    8. if(obj.hasOwnProperty(key)) {
    9. result[key] = deepClone(obj[key])
    10. }
    11. }
    12. return result
    13. }
    14. const res = deepClone(data)
    15. console.log('res', res)

    存在问题-循环引用

  • 循环引用,造成递归进入死循环导致栈内存溢出了 ```javascript const obj = { name: ‘zxc’, age: 18 } obj.obj = obj deepClone(obj)

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12492651/1623057478786-bd5f3e01-4f09-4ad9-be53-b21ba2399a14.png#clientId=u45049cda-0605-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=107&id=u05711154&margin=%5Bobject%20Object%5D&name=image.png&originHeight=214&originWidth=908&originalType=binary&ratio=2&rotation=0&showTitle=false&size=96818&status=done&style=none&taskId=uc44a58b2-bd4c-4649-b493-02c8c03fa0e&title=&width=454)
  2. <a name="VfVQz"></a>
  3. #### 解决
  4. 我们可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝,这样就巧妙化解的循环引用的问题<br /> 这个存储空间,需要可以存储key-value形式的数据,且key可以是一个引用类型,我们可以选择Map这种数据结构:
  5. - 检查map中有无克隆过的对象
  6. - - 直接返回
  7. - 没有 - 将当前对象作为key,克隆对象作为value进行存储
  8. - 继续克隆
  9. ```javascript
  10. const deepClone = (obj = {}, map = new Map()) => {
  11. if (typeof obj !== 'object' || obj == null ) {
  12. return obj
  13. }
  14. // 或者 Array.isArray(target) ? [] : {};
  15. let result = obj instanceof Array ? [] : {}
  16. if (map.get(obj)) {
  17. console.log('缓存中获取', obj);
  18. return map.get(obj);
  19. }
  20. // 反正是对象的引用,先将对象 set 进去,
  21. // 后面再实现对象属性赋值 result[key] = deepClone(obj[key], map)
  22. // 依然能得到 赋值后的对象
  23. map.set(obj, result);
  24. for (let key in obj) {
  25. if(obj.hasOwnProperty(key)) {
  26. result[key] = deepClone(obj[key], map)
  27. }
  28. }
  29. return result
  30. }

image.png

运算符 == 与 ===

image.png
注:换成 === 以上全是 false

类型判断instranceof

image.png

原型链

image.pngimage.png

image.pngimage.png
image.png

weakMap

参考

  • WeakMap结构与Map结构类似,也是用于生成键值对的集合
  • WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
  • WeakMap的键名所指向的对象,不计入垃圾回收机制。
  • 也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用

使用

  1. const wm = new WeakMap();
  2. const element = document.getElementById('example');
  3. wm.set(element, 'some information');
  4. wm.get(element) // "some information"
  1. // 对比Map对比使用
  2. // 如果我们在全局环境下定义一个对象
  3. const obj = { name: 'zxc', age: 18 }
  4. // 要想v8 回收 obj 这块使用内存,必须 obj = null
  5. obj = null
  6. //上面这样的写法显然很不方便。一旦忘了写obj = null,就会造成内存泄露。

作用

  • WeakMap结构有助于防止内存泄漏

    Map

    参考

  • 是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键

  • Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现 ```javascript var m = new Map([[‘Michael’, 95], [‘Bob’, 75], [‘Tracy’, 85]]); m.get(‘Michael’); // 95
  1. Map构造函数接受数组作为参数,实际上执行的是下面的算法
  2. ```javascript
  3. const items = [
  4. ['name', '张三'],
  5. ['title', 'Author']
  6. ];
  7. const map = new Map();
  8. items.forEach(
  9. ([key, value]) => map.set(key, value)
  10. );

初始化Map需要一个二维数组,或者直接初始化一个空MapMap具有以下方法:

  1. var m = new Map(); // 空Map
  2. m.set('Adam', 67); // 添加新的key-value
  3. m.set('Bob', 59);
  4. m.has('Adam'); // 是否存在key 'Adam': true
  5. m.get('Adam'); // 67
  6. m.delete('Adam'); // 删除key 'Adam'
  7. m.get('Adam'); // undefined

set

  • SetMap类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key
  • 要创建一个Set,需要提供一个Array作为输入,或者直接创建一个空Set

    1. var s1 = new Set(); // 空Set
    2. var s2 = new Set([1, 2, 3]); // 含1, 2, 3
  • 扩展运算符和 Set 结构相结合,就可以去除数组的重复成员。

    1. let arr = [3, 5, 2, 2, 5, 5];
    2. let unique = [...new Set(arr)];
    3. // [3, 5, 2]

    对象的浅比较与深比较

    浅比较

浅比较也称引用相等,在javascript中, ===是作浅比较,只检查左右两边是否是同一个对象的引用

深比较

深比较也称原值相等,深比较是指检查两个对象所有属性是否都相等,深比较需要以递归的方式遍历两个对象的所有属性,操作比较耗时,深比较不管这两个对象是不是同一对象的引用。

Object.assign()

参考
注意
同名属性的替换
对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加。
上面代码中,target对象的a属性被source对象的a属性整个替换掉了,而不会得到{ a: { b: 'hello', d: 'e' } }的结果。这通常不是开发者想要的,需要特别小心。
一些函数库提供 **Object.assign**的定制版本(比如 Lodash 的**_.defaultsDeep**方法),可以得到深拷贝的合并。

  1. const target = { a: { b: 'c', d: 'e' } }
  2. const source = { a: { b: 'hello' } }
  3. Object.assign(target, source)
  4. // { a: { b: 'hello' } }