js对象赋值问题(对象赋值影响原对象、对象赋值后原对象值随之变化)
js 对象赋值问题(对象赋值影响原对象)、对象赋值后原对象值随之变化、
·
问题:
一个对象赋值给另一个对象后,新对象的值更改原对象的参数值随之变化(即改变新对象的值会影响原对象值)
直接用
=
的方式把一个对象赋值给另一个对象,会导致修改新对象时,原对象也发生变化
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() 深克隆方法
更多推荐
已为社区贡献29条内容
所有评论(0)