likes
comments
collection
share

一杯茶🍵时间,琢磨一下Object.assign!

作者站长头像
站长
· 阅读数 15

Object.assign相信每一个前端人儿都有遇到过这个方法,那么它究竟是什么?Object对象的一个内置方法,它作用是所有可枚举属性的值从一个或多个源对象分配到目标对象,并返回目标对象。

// 源码中方法参数格式如下
Object.assign(target: object, ...sources: any[]): any;
  • target:目标对象。
  • sources:需要拷贝的参数。

上面只是单纯从源码本身去简单呈现Object.assign方法的构成,所以为了更好的理解Object.assign这个方法,本篇文章接下来就用详细地深入地理解它的使用与封装!

Object.assign的使用

由最近在做一些后台管理业务常遇到需要使用Object.assign这个方法的前端逻辑,比如对表单对象的属性值进行拷贝,以前一直没有深入理解Object.assign这个方法,只知道它相当于浅拷贝,但是它内部真正做了什么,真的是一窍不通😵,最近突然对它产生了🧐,所以今天花费一杯茶🍵时间,让我们一起探索它🔍。

了解之前它,我们是否都遇到下面这两个简单业务:

  • 🚀 新增一条数据:保存绑定表单的属性值,但是后端必填的有些属性是没有绑定到表单的,需要额外添加进去的,这时候就需要用到Object.assign方法的浅拷贝,也就是把额外添加的数据通过Object.assign方法拷贝到表单对象
  • 🚀 编辑已存在的数据:保存已经存在的数据内容(但是需要修改的数据),由于数据已存在,也就是说当前数据是有了唯一的id,如果我们还是按照新增的那种逻辑去保存的话,就可能会出现两种相同的数据(后端新增与修改共用一个api的情况),所以我们也需要Object.assign方法的浅拷贝,也就是把从获取信息的api中拿到的数据id通过Object.assign拷贝到表单对象

上面就是常见用到Object.assign的业务场景,那么从场景出发,我们先看看从实际开发角度的出发的使用例子:

let form = {
  Id: null,
  StudentId: null,
  Name: null,
  Age: null,
  Remark: null,
};
const studentId = 1;

// 把form对象拷贝到newForm
const newForm = Object.assign({}, form);

// 把form对象拷贝到formValues,并把studentId值赋给新对象
const formValues = Object.assign({}, form, { StudentId: studentId });

从上面例子,我们有的朋友可能就会问❓newForm与form不是一样的吗🤔、❓formValues得到的值是什么🤔

我们一个个问题来,先看第一个问题真的确定是一样吗?我们先打印一下看看看结果:

一杯茶🍵时间,琢磨一下Object.assign!

看图说话,我们就笃定这不是一样的吗,我们先考虑一个问题变量的存储,这就涉及到了变量值与引用地址了,这里就不一一深入,我们应该都知道对象属性值相同与引用地址相同两个特点同时成立才能确定对象变量相等,那我们先来分别判断一下这两个对象是否相等:

 console.log(Object.is(form, newForm));// false 只能判断这两个对象的引用地址是否一致
 console.log(JSON.stringify(form) === JSON.stringify(newForm));// true 判断这两个对象内容是否一致

通过对象内置方法is判断对象引用地址是否一致,通过JSON方法转化判断对象属性值是否相等,从打印结果来看,很明显看出Object.assign方法的作用就是赋值给目标对象,其实这就是浅拷贝的特点:只拷值不拷引用地址

一波操作下来终于会了吧!既然我们解决了第一个问题:newForm与form是不一样的,我们接着去探索一下第二个问题:formValues得到的值是什么🤔?

一杯茶🍵时间,琢磨一下Object.assign! 从上图我们可以看出其实Object.assign方法把studentId的值拷贝赋给form中的StudentId属性值,但是我们有没有想过假如把Object.assign方法中的参数位置互换一下位置结果又如何?我们来看看:

// 打印后发现formValues与form的值是一致,都是初始化状态
const formValues = Object.assign({}, { StudentId: studentId }, form);

为什么值会是一致?都是初始化状态,因为这也是Object.assign方法的一个特点:以后面的参数为主。可能有朋友更调皮一点,假如前后参数的对象属性不一样(有不一致的属性)结果又如何?我们看看:

const formValues = Object.assign({}, { StudentId: studentId, Tutor: '路灯下的光' }, form);

一杯茶🍵时间,琢磨一下Object.assign! 从上图可以看出,其实Object.assign也可以把前后不一致的属性值合并一起,这样就可以实现,把有值的对象属性拷贝给没有值的对象属性。 当然上面的例子可能还无法让大家信服,我们再看看一个例子:

let source = {
  a: {
    b: 1
  },
  c: 1
};
let target = Object.assign({}, source);

source.a.b = 2;
source.c = 3
console.log('source',source)
console.log('target',target)

一杯茶🍵时间,琢磨一下Object.assign!

从上面例子,可以看出,如果对象中的属性是基础类型,Object.assign执行的是深拷贝,source的改变不会导致目标对象的改变,如果对象中的属性是引用类型,Object.assign执行的是浅拷贝,source的改变会导致目标对象的改变。

  • 小结一下:

    🏷 Object.assign可以理解为对象中的属性是基础类型,Object.assign执行的是深拷贝;对象中的属性是引用类型,Object.assign执行的是浅拷贝。

    🏷 Object.assign除了第一个参数为目标对象,后面参数都是需要拷贝的对象。

    🏷 Object.assign拷贝对象是从第二个参数开始,后面对象融合前面对象,如果存在一致的对象属性,属性值以后面对象参数属性为主。

Object.assign的封装

上面我们已经学习了Object.assign方法的使用,接下来我们将从源码封装角度去实现Object.assign这个方法,代码如下:

const assign = function (target) {
  for (let i = 1; i < arguments.length; i++) {   //arguments为全部的对象集合
    let source = arguments[i];                 //单个对象
    //遍历一个对象的自身和继承来的属性,
    //常常配合hasOwnProperty筛选出对象自身的属性
    for (let key in source) {
      //使用call方法,避免原型对象扩展带来的干扰
      ///判断是否为source自身的属性。  
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        target[key] = source[key];
      }
    }
  }
  return target;
};

从上面代码中可以看出,拷贝的关键点在target[key] = source[key];也就是不管target中是否有source的属性,有就加进去,没有就把值拷贝到target中。我们再看看实际例子:

let form = {
  Id: null,
  StudentId: null,
  Name: null,
  Age: null,
  Remark: null,
};
const studentId = 1;

const formValues = assign({}, { StudentId: studentId, Tutor: '路灯下的光' }, form);
const newForm = assign({}, form);

console.log('form=', form);
console.log('formValues=', formValues);
console.log(Object.is(form, newForm))// false;

一杯茶🍵时间,琢磨一下Object.assign! 从结果上可以看出form对象中没有Tutor这个属性,所以没有覆盖掉它的值,而StudentId的值被form中的属性覆盖掉了,同时拷贝后的对象并不会拷贝应用地址,显而易见,原生的Object.assign方法的功能都可以在封装的assign中呈现。

  • 小结一下:

    🏷 遍历函数参数,通过遍历内置变量arguments实现。

    🏷 遍历参数,直接赋值给目标对象target。

转载自:https://juejin.cn/post/7221048707239985212
评论
请登录