写在前面,如果下面有的封装的函数没有找到,请进入我通用 工具集锦 文章查找对应函数方法
// 类型检测方法
const toType = function toType(obj) {
const reg = /^\[object ([\w\W]+)\]$/;
if (obj === null) return obj + "";
// const isObj = typeof obj === 'object' || typeof obj === 'function';
const isObj = /^(object|fuction)$/.test(typeof obj),
getObjClass = Object.prototype.toString;
// reg.exec(getObjClass.call(obj)) 返回的是一个数组,数组第二项[1]表示 第一个括号内的匹配结果
return isObj ? reg.exec(getObjClass.call(obj))[1].toLowerCase(): typeof obj
}
深浅合并
let obj1 = {
name: '张三',
age: 25,
hobby: {
music: 100,
jump: 80
}
};
// obj1.obj1 = obj1;
let obj2 = {
name: '李四',
age: 22,
sex: 0,
hobby: {
read: 100,
music: 90
}
};
let obj3 = {
name: '王五',
age: 20,
height: '158cm',
score: {
math: 100,
chinese: 90
}
};
let obj = Object.assign(obj1,obj2);
console.log(obj,obj === obj1, obj2); // 浅合并,obj === obj1为 true
不带套娃的数组或者对象的深浅合并
思路:首先进行参数分解,第一个参数如果是个boolean 类型,说明控制深浅合并的,target目标项也就是被替换项,options是之后的每一个替换项,length用来循环迭代的,i=1,表示从第二项开始,往后累加。
- 如果第一项是boolean ,说明本意想进行深合并的标识符,则第二项是被替换项(params[i]),也就是target,此时循环迭代从 ++i 开始,也就是i=2,第三项开始。
- 首先参数条件判断,如果options 有一项是空,直接进入下一次循环
- target 必须的是一个对象,不是默认空对象
- 循环迭代,每一项中,如果替换项是数组,先判断被替换项是不是数组,不是数组,默认置为空数组
- 同理替换项是对象的话,先判断被替换项是不是对象,不是对象默认置为空对象
- 如果第一项不是 boolean ,则默认浅合并,第一项是target,此时循环迭代从第二项开始,也就是 i = 1这一项
- 如果替换项存在,则直接替换
// 实现数组&对象的“深浅”合并
const merge = function merge(...params) {
let options, // 后面的替换项
target = params[0] || {}, // 最前面的被替换项
i = 1,
length = params.length,
deep = false; // 浅合并
// 第一项事布尔值:传递给 deep 控制深浅合并的
if (typeof target === 'boolean') {
deep = target;
target = params[i] || {};
i++
}
// 必须保证被替换项是一个对象
if (target == null || (typeof target !== 'object' && !isFunction(target))) target = {};
// 迭代传递的剩下的对象,一次替换target
for (; i < length; i++){
options = params[i]; // 获取某一替换项
if (options == null) continue;
// 一次拿出替换项中每一项,替换“被替换项”中同名这一项
each(options, (copy, name) => {
// name 是键名,copy 是键值——替换项
let copyIsArray = Array.isArray(copy),
copyIsObject = isPlainObject(copy),
src = target[name]; // 被替换项
if (deep && copy && (copyIsArray || copyIsObject)) {
// 深合并
if (copyIsArray && !Array.isArray(src)) src = []
if (copyIsObject && !isPlainObject(src)) src = {};
target[name] = merge(deep, src, copy); // 递归
} else if (copy !== undefined) {
// 浅合并
target[name] =copy
}
})
}
// 返回被替换这一项
return target
}
带套娃的深浅合并
思路:思路大致同不带套娃的深浅合并类似,就是多了 treated 参数,他是一个 Set 集合,里面有个 isHandler 记录被处理过的数据,如果有,则直接返回不处理了,目的防止类似 obj.obj = obj这种套娃引起的死循环。
- 第一次进行深浅拷贝,所以treated 中没有 isHandler,之后进行处理,加入到 Set 集合中,此时 treated 有 ishandler,下一次进行处理前,先从 Set 集合中先判断该数据有没有被处理过,若处理过,直接返回,没有处理,则进行循环处理,这里注意,进行迭代调用merge 时, treated 需要作为参数进行迭代
- 之后当 treated 类型为 set 数据类型,则此时参数多了一个,所以 params 的length 就需要 --length
// 实现数组&对象的“深浅”合并
const merge = function merge(...params) {
let options, // 后面的替换项
target = params[0] || {}, // 最前面的被替换项
i = 1,
length = params.length,
deep = false, // 浅合并
/*套娃 start*/
treated = params[length - 1];
// 最开始没有treated 或者递归的时候有了
if (ToType(treated) === 'set' && treated.isHandler) {
// 递归执行merge的时候
length--;
} else {
// 第一次执行 merge
treated = new Set();
treated.isHandler = true
}
/*套娃 end */
// 第一项事布尔值:传递给 deep 控制深浅合并的
if (typeof target === 'boolean') {
deep = target;
target = params[i] || {};
i++
}
// 必须保证被替换项是一个对象
if (target == null || (typeof target !== 'object' && !isFunction(target))) target = {};
// 迭代传递的剩下的对象,一次替换target
for (; i < length; i++){
options = params[i]; // 获取某一替换项
if (options == null) continue;
// 防止死递归的处理
if (treated.has(options)) return options;
treated.add(options);
// 依次拿出替换项中每一项,替换“被替换项”中同名这一项
each(options, (copy, name) => {
// name 是键名,copy 是键值——替换项
let copyIsArray = Array.isArray(copy),
copyIsObject = isPlainObject(copy),
src = target[name]; // 被替换项
if (deep && copy && (copyIsArray || copyIsObject)) {
// 深合并
if (copyIsArray && !Array.isArray(src)) src = []
if (copyIsObject && !isPlainObject(src)) src = {};
// target[name] = merge(deep, src, copy);
/*套娃 start*/ // 多了最后一项 treated
target[name] = merge(deep, src, copy,treated); // 递归
/*套娃 end */
} else if (copy !== undefined) {
// 浅合并
target[name] =copy
}
})
}
// 返回被替换这一项
return target
}
深浅拷贝
只拷贝第一层,第二层及以后都是相同地址
不带套娃的拷贝
思路:深浅拷贝中,首先参数处理,有一个参数,两个参数这两种情况,只有一个参数,说明想进行浅拷贝,有两个参数,且第一个参数是 Boolean 值,说明想进行深拷贝。
target,被拷贝的值,deep,标志位,判断是否深浅拷贝,type 被克隆值的类型,result 克隆的结果
- 先判断第一个参数是否是 boolean 值,如果是 boolean 值,且有第二个参数,则说明进行深拷贝,此时 被拷贝的项将从第二项开始,也就是 i=1 的时候
- 判断 target 的类型,如果不是数组,不是普通对象,不是函数,则typeof 类型检测为 object ,则就是标准特殊对象,和非标准特殊对象。这时创建一个相同类型的数据类型值还是该值,new target.constructor(target) 即可。
- 然后不是数组也不是对象,则是基本数据类型
- 如果是数组和普通对象,且进行深拷贝的时候,先创建空数组或者空对象,然后则进行循环迭代,结果放入先创建的数组或对象中。
// 实现深浅拷贝
const clone = function clone(...params) {
// parmas 一般也都两项,target 被拷贝得值,deep 深浅合并选项
let target = params[0],
length = params.length,
deep = false,
type,// 克隆值得类型
isArray, // 检测克隆值是否为数组,或对象
isObject,
result;
// 第一项是 boolean ,且有第二项值
if (typeof target === 'boolean' && length >1) {
deep = target;
target = params[1]
}
type = toType(target);
isArray = Array.isArray(target);
isObject = isPlainObject(target);
// 特殊值得拷贝【函数无需拷贝】
// null 和 undefined
if (target == null) return target;
if (!isArray && !isObject && !isFunction(target) && typeof target === 'object') {
// 标准特殊对象和 非标准特殊对象
// 实例的constructor,所属类原型赏的 constructor,当前构造函数本身
try {
return new target.constructor(target);
} catch (error) {
return target
}
}
// 原始值
if (!isArray && !isObject) return target;
// 数组和对象
result = new target.constructor();
each(target, (copy, name) => {
if (deep) {
// 深拷贝
result[name] = clone(deep, target)
return;
}
// 浅拷贝
result[name] = copy;
})
return result
}
带套娃的拷贝
思路: 这里和不带套娃深浅拷贝类似,只是多了一个标志位 treated ,刚开始treated 为空。刚开始第一个进行拷贝函数时,treated 为空,此时将 treated 赋值为一个空的 Set 集合,之后每处理一个数据,都将添加到 treated 中,之后每次处理数据,先从treated 中判断是否有被处理过,被处理过了则直接返回数据。
循环迭代时,注意参数传递多了一个 treated
// 实现深浅拷贝
const clone = function clone(...params) {
// parmas 一般也都两项,target 被拷贝的值,deep 深浅合并选项
let target = params[0],
length = params.length, // 注意参数个数,不多
deep = false,
i=1,
type,// 克隆值得类型
isArray, // 检测克隆值是否为数组,或对象
isObject,
treated, // set集合,记录已经处理过的内容
result;
// 第一项是 boolean ,且有第二项值
if (typeof target === 'boolean' && length >1) {
deep = target;
target = params[1];
i = 2
}
// 防止死递归处理
treated = params[i];
if (!treated) treated = new Set();
if (treated.has(target)) return target;
treated.add(target)
type = toType(target);
isArray = Array.isArray(target);
isObject = isPlainObject(target);
// 特殊值得拷贝【函数无需拷贝】
// null 和 undefined
if (target == null) return target;
if (!isArray && !isObject && !isFunction(target) && typeof target === 'object') {
// 标准特殊对象和 非标准特殊对象
// 实例的constructor,所属类原型赏的 constructor,当前构造函数本身
try {
return new target.constructor(target);
} catch (error) {
return target
}
}
// 原始值
if (!isArray && !isObject) return target;
// 数组和对象
result = new target.constructor();
each(target, (copy, name) => {
if (deep) {
// 深拷贝
result[name] = clone(deep, target,treated)
return;
}
// 浅拷贝
result[name] = copy;
})
return result
}