浅拷贝
const _shallowClone = target => {
let copy = Array.isArray(target) ? [] : {};
for (let key in target) {
if (target.hasOwnProperty(key)) {
let value = target[key];
copy[key] = value;
}
}
return copy;
};
在这段代码中,我们定义了 _shallowClone 函数,它接受一个参数 target,表示要进行浅拷贝的目标对象。
首先,我们根据 target 的类型创建一个空的副本对象 copy。如果 target 是数组,则创建一个空数组作为
copy;否则,创建一个空对象。然后,我们使用 for…in 循环遍历 target 对象的属性。对于每个属性,我们将其值赋给 value 变量,并将其复制到 copy
对象中的相应键名位置。请注意,为了确保只拷贝 target 自身的属性,我们使用了 hasOwnProperty 方法进行检查。
最后,我们返回拷贝后的 copy 对象。
简单深拷贝
const _sampleDeepClone = target => {
if (typeof target !== 'object' || target === null) {
return target;
}
const cloneTarget = Array.isArray(target) ? [] : {};
for (let key in target) {
if (target.hasOwnProperty(key)) {
if (target[key] && typeof target[key] === 'object') {
cloneTarget[key] = _sampleDeepClone(target[key]);
} else {
cloneTarget[key] = target[key];
}
}
}
return cloneTarget;
};
在这段代码中,我们定义了 _sampleDeepClone 函数,它接受一个参数 target,表示要进行简单深拷贝的目标对象。
首先,我们对 target 进行类型判断。如果 target 的类型不是对象或为 null,则直接返回 target,因为不需要进行拷贝。
然后,我们根据 target 的类型创建一个空的克隆对象 cloneTarget。如果 target 是数组,则创建一个空数组作为
cloneTarget;否则,创建一个空对象。接下来,我们使用 for…in 循环遍历 target 对象的属性。对于每个属性,我们首先使用 hasOwnProperty
方法进行检查,确保只拷贝 target 自身的属性。然后,我们判断当前属性值 target[key] 的类型。如果它是对象且不为 null,则递归调用 _sampleDeepClone
函数进行深拷贝,并将拷贝后的结果赋值给 cloneTarget[key]。否则,直接将属性值复制到 cloneTarget[key]。最后,我们返回完成深拷贝的 cloneTarget 对象。
深拷贝
if(target === null || typeof target !== 'object') return target
const constructor = target.constructor
if(/^(Function|Date|RegExp|Map|Set)$/i.test(constructor.name)) return new constructor(target)
if(map.get(target)) return map.get(target)
map.set(target, true)
const clone = Array.isArray(target) ? [] : {}
for(let key in target) {
if(Object.hasOwnProperty(key)) {
clone[key] = _completeDeepClone(target[key])
}
}
return clone
首先,我们进行了类型判断。如果 target 的类型为 null 或不是对象类型,直接返回 target,因为不需要进行深拷贝。
然后,我们获取 target 的构造函数,并使用正则表达式匹配构造函数的名称。如果构造函数的名称是
“Function”、“Date”、“RegExp”、“Map” 或 “Set”,则说明 target
是特殊类型的对象,直接通过构造函数创建一个新的实例,并将 target 作为参数传入,完成特殊对象的深拷贝。接下来,我们使用 map 来检查是否已经拷贝过 target 对象。如果已经拷贝过,直接返回之前拷贝得到的对象。
然后,我们将 target 对象存入 map 中,标记为已拷贝。
接着,我们根据 target 的类型创建一个空的克隆对象 clone。如果 target 是数组,则创建一个空数组作为
clone;否则,创建一个空对象。使用 for…in 循环遍历 target 对象的属性。对于每个属性,我们首先使用 hasOwnProperty
方法进行检查,确保只拷贝 target 自身的属性。然后,我们递归调用 _completeDeepClone 函数对属性值进行深拷贝,并将拷贝后的结果赋值给 clone[key]。
最后,我们返回完成深拷贝的 clone 对象。
这个 _completeDeepClone 函数可以处理普通对象、数组以及特殊对象(如函数、日期、正则表达式、Map 和 Set)。它使用了
map 来解决循环引用问题,确保每个对象只被拷贝一次,避免出现无限递归的情况。
发布订阅模式
请补全JavaScript代码,完成"EventEmitter"类实现发布订阅模式。
注意:
- 同一名称事件可能有多个不同的执行函数
- 通过"on"函数添加事件
- 通过"emit"函数触发事件
class EventEmitter {
constructor() {
this.events = {};
}
on(eventName, listener) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(listener);
}
emit(eventName, ...args) {
const listeners = this.events[eventName];
if (listeners) {
listeners.forEach(listener => {
listener.apply(null, args);
});
}
}
}
在构造函数中,我们初始化一个空对象 “events”,用于存储事件和对应的监听函数。
“on” 方法用于添加事件和对应的监听函数。它接受两个参数:“eventName” 表示事件名称,“listener” 表示监听函数。如果
“events” 对象中不存在该事件名称的属性,则创建一个空数组,并将监听函数添加到数组中;否则,直接将监听函数添加到已存在的数组中。“emit” 方法用于触发事件。它接受一个 “eventName” 参数和可选的其他参数。首先,我们从 “events” 对象中获取与
“eventName” 对应的监听函数数组。如果数组存在,则遍历数组,依次调用监听函数,并传入其他参数。通过使用 “on” 方法添加事件和使用 “emit”
方法触发事件,我们可以实现简单的发布订阅模式。多个监听函数可以订阅同一个事件,当触发该事件时,所有订阅该事件的监听函数都会被执行,并传入相应的参数。