lakeiedward's lakeiedward's
首页
标签
  • UI组件库

    • vue-luckyui (opens new window)
    • vue-luckyui文档 (opens new window)
  • 若依页面分层工具

    • ry-layer-page (opens new window)
    • ry-layer-page文档 (opens new window)
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 书单
    • 关于
掘金 (opens new window)
GitHub (opens new window)

lakeiedward

首页
标签
  • UI组件库

    • vue-luckyui (opens new window)
    • vue-luckyui文档 (opens new window)
  • 若依页面分层工具

    • ry-layer-page (opens new window)
    • ry-layer-page文档 (opens new window)
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 书单
    • 关于
掘金 (opens new window)
GitHub (opens new window)
  • 「Object.assign」详解 之 源码彻底吃透

    • Object.assign是什么?
      • Object.assign的使用
        • 关于深拷贝
        • 合并对象
        • 合并具有相同属性的对象
        • 原型链上的属性和不可枚举属性不能被复制
        • 基本类型会被包装为对象
      • 手写源码
        • Object.assign的兼容性
          • Object.assign的注意事项
          lakeiedward
          2022-12-02
          前端技术
          目录

          「Object.assign」详解 之 源码彻底吃透

          # Object.assign是什么?

          我们先了解一下Object.assign是什么?

          MDN的定义:Object.assign()  方法将所有可枚举和自有属性从一个或多个源对象复制到目标对象,返回目标对象。

          # Object.assign的使用

          Object.assign(target, ...sources)

          参数

          • target

            目标对象,接收源对象属性的对象,也是修改后的返回值。

          • sources

            源对象,包含将被合并的属性。

          返回值

          • 目标对象 (返回的是目标对象,即第一个参数!)

          # 关于深拷贝

          如果需要深拷贝,则需要使用其他办法,因为 Object.assign() 对于源对象里的引用数据类型是无法进行深拷贝的。假如源对象里是一个对象的引用,它仅仅会复制其引用值,并不会重新开辟一个新的内存地址。

          可以理解为,合并之后源对象里的引用数据类型是浅拷贝,基本数据类型是深拷贝。

            let obj1 = { a: 0 , b: { c: 0}};
            let obj2 = Object.assign({}, obj1);
            console.log(JSON.stringify(obj2)); // { "a": 0, "b": { "c": 0}}
          
            obj1.a = 1;//改变对象中的基本数据类型 双方不影响
            console.log(JSON.stringify(obj1)); // { "a": 1, "b": { "c": 0}}
            console.log(JSON.stringify(obj2)); // { "a": 0, "b": { "c": 0}}
          
            obj2.b.c = 3;//改变对象中的引用数据类型 双方都变了
            console.log(JSON.stringify(obj1)); // { "a": 1, "b": { "c": 3}}
            console.log(JSON.stringify(obj2)); // { "a": 2, "b": { "c": 3}}
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11

          所以说Object.assign() 并不适用于深拷贝;可以用JSON.parse(JSON.stringify())或者 递归的方式进行数据的深拷贝。

          # 合并对象

          const o1 = { a: 1 };
          const o2 = { b: 2 };
          const o3 = { c: 3 };
          
          const obj = Object.assign(o1, o2, o3);
          console.log(obj); // { a: 1, b: 2, c: 3 } -- 返回目标对象o1
          console.log(o1);  // { a: 1, b: 2, c: 3 } -- o1目标对象已经被改变了,不再是{ a: 1 };
          console.log(obj == o1); // true 
          
          1
          2
          3
          4
          5
          6
          7
          8

          如果我们想不改变源对象可以这样合并对象

          const o1 = { a: 1 };
          const o2 = { b: 2 };
          
          const obj = Object.assign({},o1, o2);
          console.log(obj); // { a: 1, b: 2 } -- 返回合并过后的目标对象
          console.log(o1);  // { a: 1} -- 源对象o1没有发生变化
          console.log(obj == o1); // false 
          
          1
          2
          3
          4
          5
          6
          7

          # 合并具有相同属性的对象

          • 合并相同属性的源对象,合并过程中后面的源对象属性会替换前面源对象的属性
          const o1 = { a: 1, b: 1, c: 1 };
          const o2 = { b: 2, c: 2 };
          
          const obj = Object.assign({}, o1, o2);
          console.log(obj); // { a: 1, b: 2, c: 2 }
          
          1
          2
          3
          4
          5

          # 原型链上的属性和不可枚举属性不能被复制

          const obj = Object.create({ foo: 1 }, { // foo 是 obj 的原型链
            bar: {
              value: 2  // bar 是不可枚举属性,因为没有设置enumerable,默认为不可枚举
            },
            baz: {
              value: 3,
              enumerable: true  // baz是可枚举的
            }
          });
          const copy = Object.assign({}, obj);
          console.log(copy); // { baz: 3 }  bar没有合并过来
          
          
          //什么是不可枚举?简单理解为是不能够一个个地列举
          let keys = []
          for (let key in obj) { //不可枚举的属性则不能遍历
            keys.push(key)
          }
          console.log(keys);//['bat', 'foo'] bar则没有遍历出来
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19

          # 基本类型会被包装为对象

          const v1 = 'abc';
          const v2 = true;
          const v3 = 10;
          const v4 = Symbol('foo');
          
          const obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
          // null 和 undefined 会被忽略
          // 只有字符串包装器才能有自己的可枚举属性
          console.log(obj); // { "0": "a", "1": "b", "2": "c" }
          
          1
          2
          3
          4
          5
          6
          7
          8
          9

          # 手写源码

          function objectAssign(target) {
            if (arguments.length === 1) return target
            let source = Array.prototype.slice.call(arguments, 1)
            source.map(object => {
              for (const key in object) {
                if (Object.hasOwnProperty.call(object, key)) {
                  target[key] = object[key];
                }
              }
            })
            return target
          }
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12

          # Object.assign的兼容性

          图片.png

          # Object.assign的注意事项

          1. 合并时,目标对象自身也会改变
          2. 拷贝是浅拷贝,不能作为深拷贝
          3. 继承属性和不可枚举属性不能拷贝
          4. 如果有同名属性的话,后面的属性值会覆盖前面的属性值
          5. 异常会打断后续拷贝任务

          如有小伙伴发现错误之处欢迎指正🤚

          #JS
          上次更新: 2022/12/02, 09:00:55
          Theme by Vdoing | Copyright © 2017-2023 lakeiedward | blog 皖ICP备2023006581号-1
          • 跟随系统
          • 浅色模式
          • 深色模式
          • 阅读模式