这是探索JS原生方法原理系列文章的第七篇。本文介绍如何实现Object.assign()方法。Object.assign()的基本用法要实现Object.assign(),首先要了解它的一般用法:接受的第一个参数表示目标对象(浅拷贝的结果),如果为null或undefined,则直接报错;如果是Object字面量或者数组,直接使用;如果是基本类型,就会装箱到对应的对象中。如果只接受第一个参数,则将其包装成一个对象,直接返回;如果接受了不止第一个参数,比如接受了第二个、第三个……等参数,那么这些参数代表源对象,它们自己的可枚举属性会一个一个的添加到目标对象中,并且具有相同名称的属性将受制于后一个对象,并且属性将被覆盖。如果第一个参数后面的参数为null或undefined,则直接跳过;在其他情况下,试图找出它们的可枚举属性,但实际上,只有字符串、数组和对象字面量是具有可枚举属性的类型。实现代码根据上面提到的思路,实现代码如下:将null或undefined转换为对象")}letres=Object(target)objs.forEach(obj=>{'usestrict'if(obj!=null&&obj!=undefined){for(letkeyinobj){if(Object.prototype.hasOwnProperty.call(obj,key)){res[key]=obj[key]}}}})returnres}Object.defineProperty(Object,'myAssign',{value:myAssign,writable:true,configurable:true,enumerable:false})注意点注意点如下:为什么不直接通过。将myAssign方法添加到对象?Object.myAssign()其实是Object的一个静态方法,但是不要直接通过.来添加,因为这样添加的方法是可枚举的,而assign()方法是不可枚举的。所以这里我们使用Object.defineProperty()来添加,同时设置方法不可枚举、可读、可配置。为什么要使用严格模式?调查参数中字符串的出现。下面两种情况很容易理解:Object.assign({a:1},"cd")//将"cd"的可枚举属性0和1添加到目标对象中,最终得到{a:1,0:"c",1:"d"}Object.assign("cd",{a:1})//将{a:1}的可枚举属性a添加到目标对象中,最后得到String{"cd",a:1}但如果是这种情况:Object.assign("ab","cd")//错误无法分配给对象'[objectString]'的只读属性'0'这里尝试放置"cd”给目标对象添加可枚举属性0和1,但问题是,目标对象String{“ab”}也有可枚举属性0和1,而且是只读的,也就是说我们尝试修改目标对象的只读属性,所以报错是合理的。然而,在非严格模式下,这种行为只是默默地失败,为了让它真正抛出错误,必须声明严格模式。为什么不使用Reflect.ownKeys(obj)?考虑到目标对象和源对象都是数组的情况,使用Reflect.ownKeys(obj)确实可以一次性获取到obj自身的可枚举属性,但是这些属性除了数组索引之外,还包括数组长度,这将导致将源对象的数组长度作为目标对象的数组长度,但实际上两者的长度不一定相等。例如,Objetc.myAssign([1,2,3],[8,9])的结果将不是预期的[8,9,3],而是[8,9],因为目标的长度对象被覆盖。为什么不直接使用obj.hasOwnProperty(key)呢?由于不能使用Reflect.ownKeys(obj),只能通过for(letkeyinobj)获取obj自身的属性+原型链属性,然后通过obj.hasOwnProperty(key)过滤掉自身的属性。但为什么不直接使用obj.hasOwnProperty(key)呢?这是因为我们对源对象了解不多。一方面,它可能会覆盖hasOwnProperty方法;另一方面,它可能基于Object.create(null),这样的对象不会继承Object原型的hasOwnProperty方法。所以这里借用Object原型的hasOwnProperty方法是最稳妥的方法。
