1.这是个什么:
Object.assign()
方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
MDN
这样说。
-
let newObj=Object.assign(target,...source)//target为目标对象 source为源对象
- 常见特性:
-
- 只能够复制源对象中可枚举的属性,继承得以及
enumerable:false
的属性都不能被拷贝。 - 对于目标对象中已经存在的属性,源对象将会根据在
arguments
中的顺序进行依次覆盖。 assign
为浅拷贝,基本类型进行值拷贝,引用类型进行址拷贝。let a={ name:132, age:44, previous:{ limit:'old' }} let b=Object.assign({},a); console.log(b); //{ name: 132, age: 44, previous: { limit: 'old' } } a.name='new name'; a.previous.limit='new'; console.log(b); //{ name: 132, age: 44, previous: { limit: 'new' } }复制代码
- 目标对象不能为
null
和undefined
,这里可以看看Object.assign()
的对于target
为null
和undefined
时的原生表现。let b={ name:132, age:44, previous:43242} let c={ previous:'dsfs0' } let d=Object.assign(null,b,c); //Cannot convert undefined or null to object复制代码
- 只能够复制源对象中可枚举的属性,继承得以及
2.实现一个Object.assign()
- 思路:
-
- 先对
target
对象进行检测是否为null || undefined
,是的话抛出TypeError
。 - 用
Object.defineProperty
定义assign2
属性。 - 用
Object()
将target
包装为一个对象to
。 - 对
arguments
中的源对象依次进行属性遍历(for...of...
)用Object.hasOwnProperty
进行属性检查,有则进行覆盖,没有则创建属性进行赋值(址)。 - 返回新对象
to
。
- 先对
- 知识储备:
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
- 这个方法会有个默认行为需注意:
Object.defineProperty(target,key,{ // enumerable:false, // writable:false, // configurable:false, })复制代码
这对我们还原assign
原生行为很重要。
Object构造函数创建一个对象包装器。
hasOwnProperty()
方法会返回一个布尔值,指示对象 自身属性中是否具有指定的属性。
该方法会判断属性是否属于对象实例,来自原型和继承得的属性将不会被遍历。
- 具体实现(直接上代码了)
if(typeof Object.assign2 !=='function'){ Object.defineProperty(Object,"assign2",{ value(target){ 'use strict' if(target==null){ //underfined null 两种非法情况 throw new TypeError("danger type error"); } let to=Object(target); for(let index=1;index<arguments.length;index++){ let nextSouce=arguments[index]; if(nextSouce!==null){ for(let nextKey in nextSouce){ if(!Object.prototype.hasOwnProperty.call(to,nextKey)){ to[nextKey]=nextSouce[nextKey]; }else{ if(to[nextKey]!==nextSouce[nextKey]){ to[nextKey]=nextSouce[nextKey]; } } } } } return to; }, writable:true, configurable:true })} 复制代码
- 要注意什么...
- 为什么使用
Object.defineProperty
来定义assign2
属性。
- 为什么使用
- 先来看一段代码
console.log(Object.keys(Object)) //[]复制代码
- 很明显看到
Object
的assign
方法是不能被遍历到的,然而如果我们直接用下面这种方式定义,我们再看一下结果。Object.assign2=function(){}console.log(Object.getOwnPropertyDescriptor(Object,'assign2'))/* { value: [Function], writable: true, enumerable: true, //注意这里 configurable: true } */ //而对于原生assign console.log(Object.getOwnPropertyDescriptor(Object,'assign'))/* { value: [Function], writable: true, enumerable: false, configurable: true } */复制代码
-
-
- 根据前面说的
Object.defineProperty
的默认行为,这里使用它就显得很合理了。
- 根据前面说的
- 为什么使用严格模式
'use strict'
- 我们先来看一个东西
let obj=Object('123');console.log(Object.getOwnPropertyDescriptor(obj,'0'))// { value: '1',// writable: false, //注意这里只读// enumerable: true,// configurable: false // }复制代码
- 对于只读的属性,
Object.assign()
对于只读属性的处理情况是let obj=Object.defineProperty({},'0',{ value:'123',})let newObj=Object.assign(obj,{0:456});console.log(newObj) //报错 TypeError: Cannot assign to read only property '0' of object '#<Object>'复制代码
- 而原生js对于只读属性修改时只会静默失败,不会报错,在严格模式下报错,所以我们这里开启严格模式。
- 我们先来看一个东西
- 前面说过
target
为null || undefined
时会报错。所以我们应该对这两种情况进行检查,为什么我们只判断了是否等于null呢,看代码console.log(null==undefined) //trueconsole.log(null === undefined) //false复制代码
- 看到这里应该理解了。
- 为什么我们用
Object.prototype.hasOwnProperty.call
来检测属性呢,来看一种情况let obj=Object.create(null);console.log(Object.getOwnPropertyDescriptor(obj,'assign'))复制代码
- 当我们以
null
为原型创建新对象时,此时创建的对象是没有指向原型链的,所以定义在Object.prototype
上的hasOwnProperty
方法就不能被访问到.
- 当我们以
-
Object.create()
方法使用现有对象作为新创建对象的原型来创建新对象