今天我们来谈一谈对象的深拷贝和浅拷贝吧
我们都知道,js数据类型主要分为两大类 基础数据类型和引用(复杂)数据类型。
简单理解就是
自己创建一个新的对象,来接受你要重新复制或引用的对象值。如果对象属性是基本的数据类型,复制的就是基本类型的值给新对象;但如果属性是引用数据类型,复制的就是内存中的地址,如果其中一个对象改变了这个内存中的地址,肯定会影响到另一个对象。
可以实现浅拷贝的方法
1、Object.assign()
var obj = {
}
var source1 = {
name: '张三',
age: 18,
like: {
music: '老鼠爱大米'
}
}
Object.assign(obj, source1)
console.log(obj)
source1.like.music= "沙漠骆驼"
console.log(obj)
// 输出
{
name: '张三', age: 18, like: {
music: '老鼠爱大米' } }
{
name: '张三', age: 18, like: {
music: '沙漠骆驼' } }
从上面可以看出,修改source1的like属性下的music属性值时,会对obj产生影响
2、扩展运算符
var obj = {
}
var source1 = {
name: '张三',
age: 18,
like: {
music: '老鼠爱大米'
}
}
obj = {
...source1 }
console.log(obj)
source1.like.music= "沙漠骆驼"
console.log(obj)
// 输出
{
name: '张三', age: 18, like: {
music: '老鼠爱大米' } }
{
name: '张三', age: 18, like: {
music: '沙漠骆驼' } }
可以发现,和Object.assign() 一样的结果
同时,使用这两种方式还需要注意
var obj = {
name: '张三',
age: 18,
sym: Symbol(123)
}
Object.defineProperty(obj, 'aaa', {
value: '这是一个不可枚举的属性',
enumerable: false // 设置为false代表此属性不可枚举
})
var obj2 = {
...obj }
console.log(obj2)
// 输出
{
name: '张三', age: 18, sym: Symbol(123) }
从上面可以看出,symbol属性被拷贝了,但是属性aaa并没有被拷贝
3、数组拷贝方法
concat ()
使用方法我就不多说了,不懂得看我上一篇文章吧
var arr = [{
name: '张三'}, 2, {
age: 18}]
var brr = arr.concat()
console.log(brr)
arr[0].name = '李四'
console.log(brr)
// 输出
[ {
name: '张三' }, 2, {
age: 18 } ]
[ {
name: '李四' }, 2, {
age: 18 } ]
可以看出,concat()也是浅拷贝
slice()
使用方法见上一篇文章
var arr = [{
name: '张三'}, 2, {
age: 18}]
var brr = arr.slice()
console.log(brr)
arr[0].name = '李四'
console.log(brr)
// 输出
[ {
name: '张三' }, 2, {
age: 18 } ]
[ {
name: '李四' }, 2, {
age: 18 } ]
和concat一样,属于浅拷贝
扩展运算符
数组扩展运算符和对象得一模一样,我就不另外说了
我们可能大多时候最常用到的深拷贝就是JSON.stringify() 了。相信大家都用过
原理就是先将对象转为json字符串,再转换为对象。
var obj = {
name: '张三',
like: {
music: '老鼠爱大米',
color: 'red',
animate: {
name: '狗'
}
}
}
var obj2 = JSON.parse(JSON.stringify(obj))
obj.like.music = '沙漠骆驼'
obj.like.animate.name = '熊猫'
console.log(obj)
console.log(obj2)
// 输出
{
name: '张三',
like: {
music: '沙漠骆驼', color: 'red', animate: {
name: '熊猫' } }
}
{
name: '张三',
like: {
music: '老鼠爱大米', color: 'red', animate: {
name: '狗' } }
}
可以看出,更改obj 不会影响到拷贝出来的obj2
但是这种方法也存在很多问题
var obj = {
base: {
name: '张三'
}
}
obj.date = new Date(0)
obj.un = undefined
obj.nu = null
obj.infinity = Infinity
obj.nan = NaN
obj.sym = Symbol(123)
obj.reg = /^123/
Object.defineProperty(obj, 'aaa', {
value: '这是一个不可枚举的属性',
enumerable: false // 设为false代表不可枚举
})
var obj2 = JSON.parse(JSON.stringify(obj))
console.log(obj)
console.log(obj2)
// 输出
{
base: {
name: '张三' },
date: 1970-01-01T00:00:00.000Z,
un: undefined,
nu: null,
infinity: Infinity,
nan: NaN,
sym: Symbol(123),
reg: /^123/
}
{
base: {
name: '张三' },
date: '1970-01-01T00:00:00.000Z',
nu: null,
infinity: null,
nan: null,
reg: {
}
}
从上面可以看出
那么,如何实现一个完整的深拷贝方法呢,那就要看我们该如何一一来解决以上问题了
function isObject (obj) {
if ((typeof obj === 'object' || typeof obj === 'function') && (obj !== null)) {
return true
}
}
function deepClone (obj) {
if (obj.constructor === Date) {
return new Date(obj) // 返回一个新实例
}
if (obj.constructor === RegExp) {
return new RegExp(obj) // 返回一个新实例
}
let allDesc = Object.getOwnPropertyDescriptors(obj)
// 遍历传入参数所有键的特性
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
for (let key of Reflect.ownKeys(obj)) {
cloneObj[key] = (isObject(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key]) : obj[key]
}
return cloneObj
}
var obj = {
base: {
name: '张三'
}
}
obj.date = new Date(0)
obj.un = undefined
obj.nu = null
obj.infinity = Infinity
obj.nan = NaN
obj.sym = Symbol(123)
obj.reg = /^123/
Object.defineProperty(obj, 'aaa', {
value: '这是一个不可枚举的属性',
enumerable: false // 设为false代表不可枚举
})
var obj2 = deepClone(obj)
console.log(obj)
console.log(obj2)
// 输出
{
base: {
name: '张三' },
date: 1970-01-01T00:00:00.000Z,
un: undefined,
nu: null,
infinity: Infinity,
nan: NaN,
sym: Symbol(123),
reg: /^123/
}
{
base: {
name: '张三' },
date: 1970-01-01T00:00:00.000Z,
un: undefined,
nu: null,
infinity: Infinity,
nan: NaN,
sym: Symbol(123),
reg: /^123/
}
从结果可以看出,我们实现的深拷贝函数确实实现了对象的一个深拷贝功能。
深拷贝与浅拷贝就写到这了。有问题欢迎指出