原生Js实现Object.assign()

更新日期: 2021-07-13阅读: 1.3k标签: object

Object.assign()应用

Object.assign()主要是将所有可枚举属性的值从一个或多个源对象复制到目标对象,同时返回目标对象

语法如下所示:

Object.assign(target, ...sources)

其中 target 是目标对象,sources 是源对象,可以有多个,返回修改后的目标对象 target。

如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后来的源对象的属性将类似地覆盖早先的属性。

示例1

我们知道浅拷贝就是拷贝第一层的基本类型值,以及第一层的引用类型地址

// 第一步
let a = {
name: "advanced",
age: 18
}
let b = {
name: "muyiy",
book: {
title: "You Don't Know JS",
price: "45"
}
}
let c = Object.assign(a, b);
console.log(c);
// {
// name: "muyiy",
// age: 18,
// book: {title: "You Don't Know JS", price: "45"}
// }
console.log(a === c);
// true

// 第二步
b.name = "change";
b.book.price = "55";
console.log(b);
// {
// name: "change",
// book: {title: "You Don't Know JS", price: "55"}
// }

// 第三步
console.log(a);
// {
// name: "muyiy",
// age: 18,
// book: {title: "You Don't Know JS", price: "55"}
// }

1、在第一步中,使用 Object.assign 把源对象 b 的值复制到目标对象 a 中,这里把返回值定义为对象 c,可以看出 b 会替换掉 a 中具有相同键的值,即如果目标对象(a)中的属性具有相同的键,则属性将被源对象(b)中的属性覆盖。这里需要注意下,返回对象 c 就是 目标对象 a。

2、在第二步中,修改源对象 b 的基本类型值(name)和引用类型值(book)。

3、在第三步中,浅拷贝之后目标对象 a 的基本类型值没有改变,但是引用类型值发生了改变,因为 Object.assign() 拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用地址

示例2

String 类型和 Symbol 类型的属性都会被拷贝,而且不会跳过那些值为 null 或undefined 的源对象。

// 第一步
let a = {
name: "muyiy",
age: 18
}
let b = {
b1: Symbol("muyiy"),
b2: null,
b3: undefined
}
let c = Object.assign(a, b);
console.log(c);
// {
// name: "muyiy",
// age: 18,
// b1: Symbol(muyiy),
// b2: null,
// b3: undefined
// }
console.log(a === c);
// true


Object.assign()实现代码

要实现 Object.assign(),首先了解它的大概用法:

接受的第一个参数表示目标对象(浅拷贝的结果),如果是 null 或者 undefined,直接报错;如果是对象字面量或者数组,直接使用;如果是基本类型,则装箱为对应的对象。
如果只接受了第一个参数,则将其包装为对象直接返回;如果不止接受了第一个参数,比如说接受了第二,第三 …… 等多个参数,那么这些参数表示源对象,它们的自身可枚举属性会一一添加到目标对象上,属性同名则以靠后的对象为准,进行属性覆盖。
第一个参数往后的参数,如果是 null 或者 undefined,那么直接跳过;其余的情况则尝试找出它们的可枚举属性,但实际上,只有字符串、数组、对象字面量这些类型是具有可枚举属性的。

根据上面所讲的思路,实现的代码就是下面这样的:

function myAssign(target,...objs){
if(target === null || target === undefined){
throw new TypeError("can not convert null or undefined to object")
}
let res = Object(target)
objs.forEach(obj => {
'use strict'
if(obj != null && obj != undefined){
for(let key in obj){
if(Object.prototype.hasOwnProperty.call(obj,key)){
res[key] = obj[key]
}
}
}
})
return res
}
Object.defineProperty(Object,'myAssign',{
value: myAssign,
writable: true,
configurable: true,
enumerable: false
})

需要注意的要点

为什么不直接通过 . 给 Object 添加 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")    
// 报错 Cannot assign to read only property '0' of object '[object String]'

这里尝试把 “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(let key in obj) 获得 obj 的自身属性 + 原型链属性,再使用 obj.hasOwnProperty(key) 筛选出自身属性了。但是为什么不直接使用 obj.hasOwnProperty(key) 呢?

这是因为,我们对源对象的情况并不了解。一方面,它可能重写了 hasOwnProperty 方法;另一方面,它可能是基于 Object.create(null) 构建的,这样的对象不会从 Object 原型上继承 hasOwnProperty 方法。所以这里借用 Object 原型的 hasOwnProperty 方法,是最保险的方式。


链接: https://www.fly63.com/article/detial/10495

Js中Object对象的理解,Object常用的属性和方法有哪些?

在JavaScript中的所有对象都是继承Object对象而来的, 我们可以理解为Object是所有js对象通用的功能,讲解Object的prototype属性、Object.prototype属性和方法?Object.prototype.constructor属性等

Object.create()的使用总汇:创建对象,参数说明,兼容性的实现

JavaScript中Object.create()的含义和语法,使用它创建null原型的对象 ,创建一个普通的空对象,Object.create()第二个参数说明。

Javascript中的object相等

相等是JavaScript中起初最让人困惑的部分。==和===的比较、强制类型的顺序等等,都使得这个问题变得复杂。今天,我们会聚焦另一个方面:object相等是如何实现的。

js 中 Object.defineProperty 的用法

还能通过 Object.defineProperty() 方法,添加或修改对象的属性。更重要的是,除了目标对象 obj,属性名称 prop 外,方法能传入属性描述符 descriptor,以实现更复杂的性质。属性描述符是一个对象,有两种形式:一种是数据描述符,另一种是存取描述符。

理解js中Object的defineProperty()和defineProperties()

Object的defineProperty和defineProperties这两个方法在js中的重要性十分重要,主要功能就是用来定义或修改这些内部属性,与之相对应的getOwnPropertyDescriptor和getOwnPropertyDescriptors就是获取这行内部属性的描述。

js中的Object.defineProperty()和defineProperties()

Object.defineProperty()该方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象,Object.defineProperties()该方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象

Object.keys方法

Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致 。

Js中Object.freeze()方法

Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。

Js Object.freeze和Object.seal

本文实例讲述了JS Object.preventExtensions(),Object.seal()与Object.freeze()用法。分享给大家供大家参考,Object.preventExtensions 只能阻止一个对象不能再添加新的自身属性,仍然可以为该对象的原型添加属性。

Object.assign实现浅拷贝的原理

什么是浅拷贝?浅拷贝就是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。浅拷贝Object.assign()是什么?主要将所有可枚举属性的值从一个或者多个数据源对象复制到目标对象,同时返回目标对象。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!