问题:
一个对象赋值给另一个对象后,新对象的值更改原对象的参数值随之变化(即改变新对象的值会影响原对象值)
直接用
=
的方式把一个对象赋值给另一个对象,会导致修改新对象时,原对象也发生变化
-
let obj1 = {
'name':
'小红'};
-
let obj2 = obj1;
-
obj2.
name =
'小明';
-
console.
log(obj1.
name);
//'小明'
原因:
JavaScript 中对象的赋值是默认引用赋值的(两个对象指向相同的内存地址),所以修改另一个对象时,即修改了内存地址里的对象,其他关联对象也会改变
解决方法(转换类型法): JSON.parse(JSON.stringify(obj))
obj2=JSON.parse(JSON.stringify(obj1))
注:
普通的对象也可以进行深拷贝,但是!!! 当对象内容项为number,string.boolean的时候,是没有什么问题的。但是,如果对象内容项为undefined,null,Date,RegExp,function,error的时候。使用JSON.stringify()进行拷贝就会出问题了。
解决方法(es6之Object.assign()法):obj2=Object.assign({},obj1)
注:
Object.assign()方法有不足之处,Object.assign()只是让对象里第一层的数据没有了关联性(即修改obj2.name时obj1.name不会发生变化),但是对象内的对象则跟被复制的对象有着关联性的(即当修改更深层的obj2.name的值时,原对象obj1.name也跟着发生了变化)
解决方法(使用递归的方式进行对象(数组)的深拷贝)
-
//已封装的深拷贝函数
-
function
deepClone(
obj = {}, hashMap = new WeakMap()) {
-
//变量先置空
-
let objClone =
null,
-
hashKey = hashMap.
get(obj);
-
if (obj
instanceof
RegExp)
return
new
RegExp(obj);
//正则表达式的情况
-
if (obj
instanceof
Date)
return
new
Date(obj);
//日期对象的情况
-
if (hashKey)
return hashKey;
-
//判断是否需要继续进行递归
-
if (
typeof obj ==
"object" && obj !==
null) {
-
objClone = obj
instanceof
Array ? [] : {};
-
hashMap.
set(obj, objClone);
-
//进行下一层递归克隆
-
for (
const i
in obj) {
-
if (obj.
hasOwnProperty(i)) {
-
objClone[i] =
deepClone(obj[i], hashMap);
-
}
-
}
-
}
else {
-
//如果不是对象直接赋值
-
objClone = obj;
-
}
-
-
return objClone;
-
}
-
//模拟数据
-
let obj = {
-
name:
"xm",
-
birth:
new
Date(),
-
desc:
null,
-
reg:
/^123$/,
-
ss: [
1,
2,
3],
-
fn:
function (
) {
-
console.
log(
"123");
-
},
-
};
-
//使用方式
-
let obj2 =
deepClone(obj);
-
obj.
fn()
//123
-
obj2.
fn()
// 123
-
console.
log(obj, obj2,
'深拷贝');
所以推荐使用深拷贝函数这种方法
解决方法(使用自定义工具库之深克隆):创建utils.js
-
/*
-
* 获取所有私有属性,包含Symbol私有属性
-
*/
-
const
getOwnProperties = obj => {
-
if (obj ===
null)
return []
-
return [
-
...
Object.
keys(obj),
// 同 ...Object.getOwnPropertyNames(obj) 获取实例的私有属性
-
...
Object.
getOwnPropertySymbols(obj)
-
]
-
}
-
-
/*
-
* 浅克隆
-
*/
-
const
shallowClone = obj => {
-
let type =
toType(obj)
-
if (
/^(string|number|boolean|null|undefined|symbol|bigint)$/.
test(type))
return obj
-
if (
/^function$/.
test(type)) {
-
return
function
proxy(
) {
-
return
obj()
-
}
-
}
-
if (
/^date|regexp$/.
test(type))
return
new obj.
constructor(
obj)
-
if (
/^error$/.
test(type))
return
new obj.
constructor(
obj.message)
-
// 只处理数组(最后返回的是数组)和对象(普通对象/类数组对象等=》最后返回的都是普通对象)
-
let keys =
getOwnProperties(obj),
-
clone = {}
-
//clone = new obj.constructor(); // 类数组的时候会有问题
-
Array.
isArray(obj) ? (clone = []) :
null
-
keys.
forEach(
key => {
-
clone[key] = obj[key]
-
})
-
return clone
-
}
-
-
/*
-
* 深克隆
-
*/
-
const
deepClone = (
obj, cache = new Set()) => {
-
// Set是一种存储结构,值的集合,保存非重复项,即Set中的元素是唯一的
-
// 只有数组和对象才处理深拷贝,其余的情况直接按照浅克隆处理即可
-
let type =
toType(obj)
-
if (!
/^(array|object)$/.
test(type))
return
shallowClone(obj)
-
-
// 避免自己套用自己导致无限递归
-
if (cache.
has(obj))
return
shallowClone(obj)
-
cache.
add(obj)
-
-
let keys =
getOwnProperties(obj),
-
clone = {}
-
type ===
'array' ? (clone = []) :
null
-
keys.
forEach(
key => {
-
clone[key] =
deepClone(obj[key], cache)
-
})
-
return clone
-
}
-
-
/*
-
* 实现两个对象的深合并(Object.assign(obj1,obj2)为浅合并)
-
* + obj1对象 obj2对象 -> 依次遍历obj2,把obj2中的每一项替换obj1中的每一项
-
* + obj1对象 obj2不是对象 -> 不进行任何处理
-
* + obj1不是对象 obj2对象 -> obj2直接替换obj1
-
* + obj1不是对象 obj2也不是对象 -> obj2直接替换obj1
-
*/
-
const
merge = (
obj1, obj2) => {
-
let isPlain1 =
isPlainObject(obj1),
-
isPlain2 =
isPlainObject(obj2)
-
if (!isPlain1)
return obj2
-
if (!isPlain2)
return obj1
-
// 遍历obj2中的每一项,让其替换obj1中的每一项
-
let obj2Arr =
getOwnProperties(obj2)
-
obj2Arr.
forEach(
key => {
-
obj1[key] =
merge(obj1[key], obj2[key])
-
})
-
return obj1
-
}
-
-
//===========================================================
-
-
// 数据类型检测通用方法
-
let getProto =
Object.
getPrototypeOf,
// 获取实例的原型对象
-
class2type = {},
-
toString = class2type.
toString,
// 取Object.prototype.toString
-
hasOwn = class2type.
hasOwnProperty,
// 取Object.prototype.hasOwnProperty
-
fnToString = hasOwn.
toString,
// 取Function.prototype.toString 转换字符串用
-
ObjectFunctionString = fnToString.
call(
Object)
// 同Object.toString() 把Object函数转成字符串 "function Object() {[native code]}"
-
-
/*
-
* 循环数据中的每一项:建立数据类型检测的映射表
-
* + [object String]/[object Number]/[object Boolean]都是为了处理基于构造函数创建的基本数据值的引用类型值,最后期许检测出来的结果依然是"string/number/boolean"
-
* + typeof new Number(10) => "object"
-
* + toString.call(new Number(10)) => "[object Number]"
-
*/
-
let assembleKeys = [
-
'String',
-
'Number',
-
'Boolean',
-
'Symbol',
-
'Function',
-
'Array',
-
'Object',
-
'Date',
-
'RegExp',
-
'Error',
-
'GeneratorFunction'
-
]
-
assembleKeys.
forEach(
name => (class2type[
`[object ${name}]`] = name.
toLowerCase()))
-
-
/*
-
* 检测数据类型的公共方法
-
*/
-
const
toType = obj => {
-
// '=='判断null或者undefined,+""转成字符串 "null"或者"undefined"
-
if (obj ==
null)
return obj +
''
-
// 如果是引用数据类型(包含:new Number(10)这种),则基于Object.prototype.toString检测(拿检测的结果到之前建立的映射表中去匹配查找,找到对象的小写数据类型,找不到则返回"object");而基本数据类型,之前排除了null/undefined,剩下的基于typof即可解决
-
return
typeof obj ===
'object' ||
typeof obj ===
'function'
-
? class2type[toString.
call(obj)] ||
'object'
-
:
typeof obj
-
}
-
-
/*
-
* 检测是否为一个纯粹对象
-
*/
-
const
isPlainObject = obj => {
-
let proto =
null,
-
Ctor,
-
type =
toType(obj);
-
if (obj ===
undefined)
return
false
-
// 不存在或者检测数据类型的结果都不是object,则一定不是纯粹对象
-
if (!obj || type !==
'object')
return
false;
-
// 不存在原型的情况:Object.create(null),相当于创建空对象{}
-
proto =
getProto(obj)
-
if (!proto)
return
true;
-
// 获取当前值原型对象上的constructor(即获取它的构造函数)
-
Ctor = hasOwn.
call(proto,
'constructor') && proto.
constructor;
-
// 有构造函数,并且构造函数需要直接是Object才可以(这样排除了NodeList等子类/自定义类的实例)
-
// ObjectFunctionString === fnToString.call(Object)
-
return
typeof
Ctor ===
'function' && fnToString.
call(
Ctor) ===
ObjectFunctionString
-
}
-
-
-
export
default {
-
getOwnProperties,
-
shallowClone,
-
deepClone,
-
toType,
-
isPlainObject,
-
merge
-
}
工具库使用方法
-
import utils
from
'@/libs/utils'
// vue文件引入utils.js
-
-
obj2 = utils.
deepClone(obj1)
// deepClone() 深克隆方法