Object.assign()
和递归函数,后面会用到
Object.assign()
Object.assign()
是一个用于将所有可枚举的自身属性从一个或多个源对象复制到目标对象的方法,返回目标对象。它主要用于合并对象,或者克隆对象。
基本语法如下:
Object.assign(target, ...sources)
其中:
target
是目标对象,也是最后返回的对象。...sources
是一个或多个源对象。这里有一个简单的例子:
let obj1 = { a: 1 };
let obj2 = { b: 2 };
let obj3 = { c: 3 };
let obj = Object.assign({}, obj1, obj2, obj3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(obj1); // { a: 1 }, 属性 'b' 和 'c' 没有复制到 obj1
console.log(obj2); // { b: 2 }, 属性 'a' 和 'c' 没有复制到 obj2
console.log(obj3); // { c: 3 }, 属性 'a' 和 'b' 没有复制到 obj3
在这个例子中,Object.assign()
方法将obj1
、obj2
和obj3
对象的所有可枚举的自身属性复制到空对象中,并返回这个空对象。需要注意的是,如果源对象的某个属性同名于目标对象已有的属性,那么,该属性将不会被复制到目标对象中。
以下是setTimeout()
利用递归函数模拟实现setInterval()
的效果:
function getTime() {
document.querySelector('div').innerHTML = new Date().toLocaleString()
setTimeout(getTime, 1000)
}
getTime()
深拷贝和浅拷贝只针对引用数据类型,因为简单数据类型赋值给接受变量,改变接受变量的值不会影响原变量
浅拷贝:拷贝的是地址,对于简单数据类型是直接将值复制一份给接受变量,但是对于复杂数据类型还是将地址传过去,对于含有对象的对象,浅拷贝就会出问题 (简单理解: 如果是单层对象,没问题,如果有多层就有问题)
常见方法:
Object.assgin()
; 展开运算符 {...obj}
concat()
;截取slice()
; 展开运算符[...arr]
const obj = {
uname: 'jack',
age: 18,
}
const o = { ...obj }
console.log(o) // {uname:'jack',age:'18'}
o.age = 20
console.log(o) // {uname:'jack',age:'20'}
console.log(obj) // {uname:'jack',age:'18'}
// 这样就解决了会改变源对象属性的问题
浅拷贝会出现的问题:
浅拷贝,因为拷贝的是复杂数据类型的地址,
const obj = {
uname: 'jack',
age: 18,
family: {
baby: '小jack'
}
}
const o = {}
Object.assign(o, obj)
o.age = 20 // 改变拷贝后对象中的属性age的值
o.family.baby = '老jack' // 改变拷贝后对象中的对象的属性baby的值
console.log(o) // { uname:'jack',age:'20',family:{baby:'老jack'}} 拷贝后的对象
console.log(obj) // { uname:'jack',age:'18',family:{baby:'老jack'}} 原对象
上面中的代码就发现了问题,改变浅拷贝后对象的属性值,不会影响原对象。但是改变对象中的对象的属性值,会影响原对象。为了解决这个问题,所以我们有了更深层次的拷贝,所以有了下面的深拷贝。
JSON.stringify
与 JSON.parse
进行深拷贝 缺点 忽略函数、属性值为undefined的属性
const obj = {
name: 'longge',
age: 16,
arr: [],
o: { a: 1, b: 2 },
}
const newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)
以下是个简单的自己利用递归实现的深度拷贝:
// 定义一个对象,名为obj,包含四个属性:uname、age、hobby和family,其中family对象中又包含一个baby属性
const obj = {
uname: 'jack', // 属性uname的值是'jack'
age: 18, // 属性age的值是18
hobby: ['乒乓球', '足球'], // 属性hobby的值是一个数组,包含两个字符串元素:'乒乓球'和'足球'
family: { // 属性family的值是一个对象
baby: '小jack' // family对象中的baby属性的值是'小jack'
}
}
// 定义一个空对象,名为o
const o = {}
// 定义一个函数,名为deepCopy,接受两个参数:newObj和oldObj
function deepCopy(newObj, oldObj) {
debugger // 使用debugger关键字,可以在JavaScript运行时创建一个断点,方便进行调试
for (let k in oldObj) { // 遍历oldObj对象的每一个属性
// 如果属性k的值是一个数组
if (oldObj[k] instanceof Array) {
// 在newObj中创建一个空数组,名为k
newObj[k] = []
// 对这个新创建的数组进行深度复制
deepCopy(newObj[k], oldObj[k])
// 如果属性k的值是一个对象
} else if (oldObj[k] instanceof Object) {
// 在newObj中创建一个空对象,名为k
newObj[k] = {}
// 对这个新创建的对象进行深度复制
deepCopy(newObj[k], oldObj[k])
// 如果属性k的值不是数组也不是对象
} else {
// 将oldObj的属性k赋值给newObj的属性k,实现浅复制
newObj[k] = oldObj[k]
}
}
}
// 对o和obj进行深度复制,复制结果存储在o中
deepCopy(o, obj)
// 在控制台打印对象o的内容
console.log(o)
// 修改o的age属性为20
o.age = 20
// 修改o的hobby属性,将第一个元素修改为'篮球'
o.hobby[0] = '篮球'
// 修改o的family属性的baby属性为'老jack'
o.family.baby = '老jack'
// 在控制台打印原始对象obj的内容,可以看到obj并未因为对o的修改而改变
console.log(obj)
Lodash
实现深拷贝可以使用第三方封装的库进行深拷贝,其中就有Lodash
,具体使用可以参考官网的使用文档:Lodash中文文档