对象的浅合并与深合并、浅拷贝与深拷贝

您所在的位置:网站首页 不能使用join命令合并的对象 对象的浅合并与深合并、浅拷贝与深拷贝

对象的浅合并与深合并、浅拷贝与深拷贝

2023-06-30 23:40| 来源: 网络整理| 查看: 265

在学习对js的array、number、objects、string的操作时,我们可以对比loadash工具库的实现,在实际开发过程中进行使用。

在开始之前我们先明确一个概念,js的栈与堆。栈与堆都是js的数据结构,栈的空间较小,用来存储基础类型的数据,例如string,number;堆空间较大,用来存储大的数据,例如function、object等。

在执行js的时候,会先创建一个上下文,这里就是一个栈结构

var a = 1 var b = {} // 上下文环境存储的是 // 键 a : 值 1 // 键 b : 值 对象的引用地址 复制代码 一、浅合并

浅合并通俗来理解就是只合并第一层,不对相同属性做递归合并处理,而是直接替换,比如以下示例

let obj1 = { a: 'a1', b: 'b1', c: { d: 'c1', e: 'c2' } } let obj2 = { a: 'a2', c: { d: 'c3' } } console.log(shallowMerge(obj1, obj2)) // 输出 { a: 'a2', b: 'b1', c: { d: 'c3' } } 复制代码

实现浅合并思想如下:

边界值判定,如果object1不是对象,使用object2替换object1 如果object1是对象,object2不是对象,返回object1 如果object1是对象且object2是对象,遍历object2进行合并,遇到相同属性直接进行替换

js已经替我们实现了Object.assign,但是会有一些边界值问题,我们可以进行自定义实现

// 是否是引用类型,具体指使用typeof类型为object,并且不是null的值 function isObjectLike(value) { return typeof value === 'object' && value !== null } // 使用Object.prototype.toString获取表示该对象的字符串,例如[object Array] // 在es5之后,toString方法已经可以返回正确的类型,null对应[object Null] const toString = Object.prototype.toString function getTag(value) { return toString.call(value) } // 判断是否是普通对象,typeof性能会更好 function isPlainObject(value) { if (!isObjectLike(value) || getTag(value) != '[object Object]') { return false } // 例如:Object.create(null) if (Object.getPrototypeOf(value) === null) { return true } // 循环遍历对象,如果是自定义构造器实例化的object则返回false let proto = value while (Object.getPrototypeOf(proto) !== null) { proto = Object.getPrototypeOf(proto) } return Object.getPrototypeOf(value) === proto } // 浅合并 shallowMerge (obj1, obj2) { let isPlain1 = isPlainObject(obj1) let isPlain2 = isPlainObject(obj2) // 1.边界值判定,如果object1不是对象,使用object2替换object1 if(!isPlain1){ return obj2 } // 2.如果object1是对象,object2不是对象,返回object1 if(!isPlain2){ return obj1 } // 3.如果object1是对象且object2是对象,遍历object2进行合并 // Object.keys 获取可枚举普通键 // Object.getOwnPropertyNames 获取除symbol以外的所有键 // Object.getOwnPropertySymbols 获取symbol键 // 根据Object.assign定义,它会合并Object.keys与Object.getOwnPropertySymbols的所有值 [...Object.keys(obj2),...Object.getOwnPropertySymbols(obj2)].forEach((key)=>{ obj1[key] = obj2[key] }) return obj1 } 复制代码 二、深合并

深合并也就是要进行递归合并,将对象的所有子属性也进行合并,代码演示如下:

let obj1 = { a: 'a1', b: 'b1', c: { d: 'c1', e: 'c2' } } let obj2 = { a: 'a2', c: { d: 'c3' } } console.log(deepMerge(obj1, obj2)) // 输出 { a: 'a2', b: 'b1', c: { d: 'c3', e: 'c2' } } 复制代码

实现深合并思想如下:

边界值判定,如果object1不是对象,使用object2替换object1 如果object1是对象,object2不是对象,返回object1 如果object1是对象且object2是对象,遍历object2进行合并,遇到键相同的普通对象值,递归合并,其他直接进行替换 与浅拷贝唯一的区别就是赋值的时候判断一下值类型,进行递归调用 deepMerge (obj1, obj2) { let isPlain1 = isPlainObject(obj1) let isPlain2 = isPlainObject(obj2) if(!isPlain1){ return obj2 } if(!isPlain2){ return obj1 } [...Object.keys(obj2),...Object.getOwnPropertySymbols(obj2)].forEach((key)=>{ //与浅拷贝区别之处 obj1[key] = deepMerge(obj1[key],obj2[key]) }) return obj1 } 复制代码 三、浅拷贝

浅拷贝也就是只拷贝一层,对于非基础类型直接引用,其中任一对象属性的改变会影响到另一对象,示例如下:

let obj1 = { a: 'a1', c: { d: 'c1' } } let obj2 = shallowClone(obj1) obj1.a = 'a2' obj1.c.d = 'c2' console.log(obj2) // 输出 { a: 'a1', c: { d: 'c2' } } 复制代码

浅拷贝实现思想

对obj1进行遍历,然后将基础类型值直接赋值给obj2,将引用类型的引用地址赋值给obj2 shallowClone(obj){ let result = {} // for...in以任意顺序遍历一个对象的除[Symbol]以外的[可枚举]属性,包括继承的可枚举属性 for (const key in obj){ // hasOwnProperty会返回一个布尔值,指示对象自身属性中是否具有指定的属性 if(obj.hasOwnProperty(key)){ result[key] = obj[key] } } return result } 复制代码 三、深拷贝

深拷贝就是对对象所有属性进行递归遍历,且拷贝对象之间的值不会有相互影响。 示例如下

let obj1 = { a: 'a1', c: { d: 'c1' } } let obj2 = deepClone(obj1) obj1.a = 'a2' obj1.c.d = 'c2' console.log(obj2) // 输出 { a: 'a1', c: { d: 'c1' } } 复制代码

最简单的深拷贝使用Object.parse(Object.stringfy()),缺点是不能处理函数与正则,拷贝出来的值会变成null与空对象。

深拷贝实现思想

要对子元素的类型先进行分类 如果是基础类型则直接赋值 如果是引用类型则根据不同情况做处理 对于普通对象做递归拷贝 // 将所有类型罗列 // 基础类型 const stringTag = '[object String]' const symbolTag = '[object Symbol]' const numberTag = '[object Number]' const boolTag = '[object Boolean]' const nullTag = '[object Null]' const undefinedTag = '[object Undefined]' // 内置对象 const argsTag = '[object Arguments]' const arrayTag = '[object Array]' const dateTag = '[object Date]' const errorTag = '[object Error]' const mapTag = '[object Map]' const objectTag = '[object Object]' const regexpTag = '[object RegExp]' const setTag = '[object Set]' const weakMapTag = '[object WeakMap]' ... function deepClone(obj, hash = new WeakMap()) { // 防止循环引用,缓存引用地址,如果出现过则直接返回结果 if (hash.get(obj)) return hash.get(obj); // 基础类型与function直接返回值,function的拷贝没有考虑this指向问题 if(typeof obj !== 'object' || obj === null){ return obj } // 对日期和正则做特殊处理,如果数据复杂,可以针对需要支持的内置对象做响应处理 if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); // 对其他引用对象做处理 let cloneObj = new obj.constructor() hash.set(obj, cloneObj); for (let key in obj) { if (obj.hasOwnProperty(key)) { // 实现一个递归拷贝 cloneObj[key] = deepClone(obj[key]) } } return cloneObj; } 复制代码

没有尽善尽美的深拷贝,知晓其支持程度及相应优缺点用来支持业务功能。



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3