简单响应式的实现(1)

设置addFn函数和fnList函数数组

const fnList = [] // fn arrayfunction addFn(fn) { // add fn to fnList fnList.push(fn) }const obj = { // target object name: 'lisa' }// fn - 1 addFn(() => { console.log('first fn') })// fn - 2 addFn(() => { console.log('second fn') })obj.name = 'peter' fnList.forEach(fn => { fn() })

上述代码中的fnList是用于存储当属性发生变化时所要执行的函数。addFn函数就是一个将对属性有依赖的函数添加至fnList函数数组中。但是,你会发现现在还并不完全,而且还是通过手动调动起来的。下面一步一步的来进行完善。
设置一个类depend
我们可以专门设置一个depend类来进行依赖函数的添加和调用。
class Depend { constructor() { this.fnList = [] }addDependFn(fn) { this.fnList.push(fn) }notify() { this.fnList.forEach(fn => fn()) } }const depend =new Depend() const obj = { name: 'lisa' }depend.addDependFn(() => { console.log('first fn') })depend.addDependFn(() => { console.log('second fn') })obj.name = 'peter'depend.notify()

这个时候,我们就把一系列的操作封装了起来,但是现在还是要通过我们手动进行调用。
使用Proxy和Reflect进行处理
我们可以使用Proxy对目标对象进行代理,然后再使用Reflect进行对象的操作。
class Depend { constructor() { this.fnList = [] }addDependFn(fn) { this.fnList.push(fn) }notify() { this.fnList.forEach(fn => fn()) } }const depend =new Depend() const obj = { name: 'lisa' }const proxyObj = new Proxy(obj, { get(target, key, receiver) { return Reflect.get(target, key, receiver) },set(target, key, newValue, receiver) { depend.notify() Reflect.set(target, key, newValue, receiver) } })depend.addDependFn(() => { console.log('first fn') })depend.addDependFn(() => { console.log('second fn') })proxyObj.name = 'peter'

【简单响应式的实现(1)】这个时候,当objProxy中的属性值发生了,那么就会被set捕获器所监听到,notify()方法就被调用。
利用Map和WeakMap进行数据存储
我们可以按照下面图中所示来进行存储管理,weakMap来存储所有的‘可响应式’对象,而map又来存储对象当中的属性。
简单响应式的实现(1)
文章图片

根据上面图中所画的存储结构,可以把我们的代码整理一下:
class Depend { constructor() { this.fnList = [] }addDependFn(fn) { this.fnList.push(fn) }notify() { this.fnList.forEach(fn => fn()) } }// 创建obj中属性name和obj的depend对象 const dependObjName =new Depend() const dependObjAge = new Depend() const obj = { name: 'lisa', age: 18 }// 创建info中属性address的depend对象 const dependInfoAddress = new Depend() const info = { address: 'sichuan' }// 创建一个map,用来存储obj中各属性的depend const mapObj = new Map() mapObj.set('name', dependObjName) mapObj.set('age', dependObjAge)// 创建一个map,用来存储info中各属性的depend const mapInfo = new Map() mapInfo.set('address', dependInfoAddress)// 创建一个weakMap,用来存储obj和info的map const allMap = new WeakMap() allMap.set(obj, mapObj) allMap.set(info, mapInfo)// obj proxy const proxyObj = new Proxy(obj, { get(target, key, receiver) { return Reflect.get(target, key, receiver) },set(target, key, newValue, receiver) { const map = allMap.get(target) // 获取存储obj的map const depend = map.get(key) // 获取当前key所对应depend depend.notify() Reflect.set(target, key, newValue, receiver) } })// info proxy const proxyInfo = new Proxy(info, { get(target, key, receiver) { return Reflect.get(target, key, receiver) },set(target, key, newValue, receiver) { const map = allMap.get(target) // 获取存储obj的map const depend = map.get(key) // 获取当前key所对应depend depend.notify() Reflect.set(target, key, newValue, receiver) } })// obj name depend fn dependObjName.addDependFn(() => { console.log('obj1 name first fn') })dependObjName.addDependFn(() => { console.log('obj1 name second fn') })// obj age depend fn dependObjAge.addDependFn(() => { console.log('obj age first fn') })dependObjAge.addDependFn(() => { console.log('obj age second fn') })// info address depend fn dependInfoAddress.addDependFn(() => { console.log('info address first fn') })proxyObj.name = 'peter' proxyObj.age = 18proxyInfo.address = 'chongqing'

我们也可以把proxy中的获取depend对象的步骤封装成一个函数:
/** * * @param {*} target 目标对象 * @param {*} key 对象中属性名 * @returns key所对应的depend对象 */ function getDepend(target, key) { // 当map和depend不存在时进行创建的目的:可以避免出现undefined.notify()出现并报错 let map = allMap.get(target) // 获取target所对应的map if(!map) { map = new Map() // 如果所获取到的map为undefined,那么就创建一个map allMap.set(target, map) } let depend = map.get(key) // 获取key值所对应的depend对象 if(!depend) { depend = new Depend()// 如果所获取到的depend为undefined,那么就创建一个depend对象 map.set(key, depend) } return depend }

现在我们可以做到外界修改对象中的值时,将所依赖的函数就行执行,但是还差的就是依赖函数的收集等内容。

    推荐阅读