1.手写深拷贝函数
前面我们已经学习了对象相互赋值的一些关系,分别包括:
- 引入的赋值:指向同一个对象,相互之间会影响;
- 对象的浅拷贝:只是浅层的拷贝,内部引入对象时,依然会相互影响;
- 对象的深拷贝:两个对象不再有任何关系,不会相互影响;
JavaScript并没有给我们提供深拷贝的方法, 因为深拷贝是非常消耗内存的
前面我们已经可以通过JSON的方法来实现深拷贝了:JSON.parse
- 这种深拷贝的方式其实对于函数、Symbol等是无法处理的;
- 并且如果存在对象的循环引用,也会报错的;
那么, 如果实际开发中真的需要深拷贝, 当然这种情况是非常少的, 但是如果真的需要, 我们就需要自定义深拷贝函数了
手写深拷贝我们按照以下步骤:
- 自定义深拷贝的基本功能;
- 其他数据类型的值进程处理:数组、函数、Symbol、Set、Map;
- 对Symbol的key进行处理;
1.1 基本功能实现
首先按照我们实现一个工具函数的步骤:
- 需要接收什么参数
- 返回值是什么
- 内部实现
1.需要接收的参数
-
接收一个对象作为参数, 对这个传入的对象进行深拷贝
function deepCopy(obj) { }
2.返回值是什么
-
返回一个已经完成深拷贝的新对象
function deepCopy(obj) { const newObj = {} return newObj }
3.内部实现
-
我们思考一下, 如何判断是否是一个对象呢, 大家应该都想到了typeof, 但是我们每个地方都使用typeof是有点麻烦的, 而且如果传入的对象为null(null也是一个对象), 我们想要返回false
-
一般遇到这种情况, 我们会单独封装一个小工具函数用来判断是否是一个对象, 这也是很多库和框架的做法
function isObject(value) { const valueType = typeof value return (valueType !== null) && (valueType === "function" || valueType === "object") }
-
然后我们再实现内部代码
// 判断是否是对象 function isObject(value) { const valueType = typeof value return (value !== null) && (valueType === "function" || valueType === "object") } // 深拷贝函数 function deepCopy(obj) { // 1.如果是原始类型, 直接返回即可 if (!isObject(obj)) return obj // 2.如果是对象类型,需要创建对象 const newObj = {} // 3.对传入的对象进行遍历 for (const key in obj) { // 4.对传入对象的属性值进行递归调用 newObj[key] = deepCopy(obj[key]) } return newObj }
-
这样深拷贝的基本功能已经实现, 我们测试一下
const info = { name: "kaisa", age: 18, friend: { name: "chen", address: { province: "四川省", city: "成都市" } } } // 进行深拷贝 const newInfo = deepCopy(info) // 测试 console.log(newInfo) // 修改原对象, 不会影响新对象 info.friend.address.province = "重庆市" console.log(info.friend.address.province) // 重庆市 console.log(newInfo.friend.address.province) // 四川省
1.2 其他数据类型值处理
-
特殊类型数组: 我们在传入值的时候, 有可能传入一个数组对象, 传入数组的话我们只需添加一行代码就可以解除这个问题
function isObject(value) { const valueType = typeof value return (value !== null) && (valueType === "function" || valueType === "object") } // 深拷贝函数 function deepCopy(obj) { if (!isObject(obj)) return obj // 判断是否是一个数组, 如果是创建数组, 不是创建对象 const newObj = Array.isArray(obj) ? [] : {} for (const key in obj) { newObj[key] = deepCopy(obj[key]) } return newObj }
-
特殊类型Set, Map: 如果是Set, Map类型我们也需要做对应处理(这里只写一个set)
function isObject(value) { const valueType = typeof value return (value !== null) && (valueType === "function" || valueType === "object") } // 深拷贝函数 function deepCopy(obj) { if (!isObject(obj)) return obj // 2.判断是否是set类型 if (obj instanceof Set) { const newSet = new Set() for (setItem of obj) { newSet.add(deepCopy(setItem)) } return newSet } // 1.判断是否是一个数组, 如果是创建数组, 不是创建对象 const newObj = Array.isArray(obj) ? [] : {} for (const key in obj) { newObj[key] = deepCopy(obj[key]) } return newObj }
-
特殊类型函数: 函数在开发中是用来执行的, 所以我们函数我们不需要进行深拷贝, 我们对函数也需处理一下
function isObject(value) { const valueType = typeof value return (value !== null) && (valueType === "function" || valueType === "object") } // 深拷贝函数 function deepCopy(obj) { if (!isObject(obj)) return obj // 2.判断是否是set类型 if (obj instanceof Set) { const newSet = new Set() for (setItem of obj) { newSet.add(deepCopy(setItem)) } return newSet } // 3.判断是否是函数, 是函数直接返回 if (typeof obj === "function") { return obj } // 1.判断是否是一个数组, 如果是创建数组, 不是创建对象 const newObj = Array.isArray(obj) ? [] : {} for (const key in obj) { newObj[key] = deepCopy(obj[key]) } return newObj }
-
特殊类型值为Symbol: 值为Symbol我们也需要进行单独处理
function isObject(value) { const valueType = typeof value return (value !== null) && (valueType === "function" || valueType === "object") } // 深拷贝函数 function deepCopy(obj) { // 4.判断是否是Symbol类型 if (typeof obj === "symbol") { return Symbol(obj.description) } if (!isObject(obj)) return obj // 2.判断是否是set类型 if (obj instanceof Set) { const newSet = new Set() for (setItem of obj) { newSet.add(deepCopy(setItem)) } return newSet } // 3.判断是否是函数, 是函数直接返回 if (typeof obj === "function") { return obj } // 1.判断是否是一个数组, 如果是创建数组, 不是创建对象 const newObj = Array.isArray(obj) ? [] : {} for (const key in obj) { newObj[key] = deepCopy(obj[key]) } return newObj }
-
特殊类型key为Symbol:key为Symbol时, for…in不会遍历Symbol, 我们需要对Symbol单独进行遍历
function isObject(value) { const valueType = typeof value return (value !== null) && (valueType === "function" || valueType === "object") } // 深拷贝函数 function deepCopy(obj) { // 4.判断是否是Symbol类型 if (typeof obj === "symbol") { return Symbol(obj.description) } if (!isObject(obj)) return obj // 2.判断是否是set类型 if (obj instanceof Set) { const newSet = new Set() for (setItem of obj) { newSet.add(deepCopy(setItem)) } return newSet } // 3.判断是否是函数, 是函数直接返回 if (typeof obj === "function") { return obj } // 1.判断是否是一个数组, 如果是创建数组, 不是创建对象 const newObj = Array.isArray(obj) ? [] : {} for (const key in obj) { newObj[key] = deepCopy(obj[key]) } // 5.单独遍历Symbol const symbolKeys = Object.getOwnPropertySymbols(obj) for (const symbolKey of symbolKeys) { newObj[Symbol(symbolKey.description)] = deepCopy(obj[symbolKey]) } return newObj }
如果还有其他特殊类型需要处理, 按照上面的逻辑追加即可
2.手写事件总线
自定义事件总线属于一种观察者模式,其中包括三个角色:
- 发布者(Publisher):发出事件(Event);
- 订阅者(Subscriber):订阅事件(Event),并且会进行响应(Handler);
- 事件总线(EventBus):无论是发布者还是订阅者都是通过事件总线作为中台的;
当然我们可以选择一些第三方的库:
- Vue2默认是带有事件总线的功能;
- Vue3中推荐一些第三方库,比如mitt;
我们也可以实现自己的事件总线, 按照以下步骤:
- 事件的监听方法on;
- 事件的发射方法emit;
- 事件的取消监听off;
我们创建一个类作为事件总线对象
// 类EventBus -> 事件总线对象
class HYEventBus {
constructor() {
this.eventMap = {}
}
on(eventName, eventFn) {
let eventFns = this.eventMap[eventName]
if (!eventFns) {
eventFns = []
this.eventMap[eventName] = eventFns
}
eventFns.push(eventFn)
}
off(eventName, eventFn) {
let eventFns = this.eventMap[eventName]
if (!eventFns) return
for (let i = 0; i < eventFns.length; i++) {
const fn = eventFns[i]
if (fn === eventFn) {
eventFns.splice(i, 1)
break
}
}
// 如果eventFns已经清空了
if (eventFns.length === 0) {
delete this.eventMap[eventName]
}
}
emit(eventName, ...args) {
let eventFns = this.eventMap[eventName]
if (!eventFns) return
eventFns.forEach(fn => {
fn(...args)
})
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/120109.html