![由浅入深,带你用JavaScript实现响应式原理](http://img.jiangsulong.com/220401/1626345b2-8.jpg)
文章插图
5.7.对象的依赖收集优化
可以发现上面打印的结果中的响应式函数数组全部为空 , 是因为在前面收集响应式函数是通过watchFn来收集的 , 而在getDepend中并没有去收集对应的响应式函数 , 所以返回的dep对象里面的数组全部就为空了 。如果对响应式函数 , 还需要通过自己一个个去收集 , 是不太容易的 , 所以可以监听响应式函数中依赖了哪一个对象属性 , 让Proxy的get捕获器去收集就行了 。
- 既然get需要监听到响应式函数访问了哪些属性 , 那么响应式函数在被添加之前肯定是要执行一次的;
- 如何在Proxy中拿到当前需要被收集的响应式函数呢?可以借助全局变量;
- 下面就来对watchFn进行改造;
// 定义一个全局变量 , 存放当前需要收集的响应式函数let currentReactiveFn = nullfunction watchFn(fn) {currentReactiveFn = fn// 先调用一次函数 , 提醒Proxy的get捕获器需要收集响应式函数了fn()// 收集完成将currentReactiveFn重置currentReactiveFn = null}
Proxy中get捕获器具体需要执行的操作:// 创建一个Proxyconst objProxy = new Proxy(obj, {get: function(target, key, receiver) {const dep = getDepend(target, key)// 拿到全局的currentReactiveFn进行添加dep.addDependFn(currentReactiveFn)return Reflect.get(target, key, receiver)},set: function(target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)// 根据当前对象target和设置的key , 去获取对应的depconst dep = getDepend(target, key)console.log(dep)// 当set捕获器捕获到属性变化时 , 自动去调用notifydep.notify()}})
下面测试一下看看效果:watchFn(function() {console.log('1:我依赖了name属性')console.log(objProxy.name)})watchFn(function() {console.log('2:我依赖了name属性')console.log(objProxy.name)})watchFn(function() {console.log('1:我依赖了age属性')console.log(objProxy.age)})watchFn(function() {console.log('2:我依赖了age属性')console.log(objProxy.age)})console.log('----------以上为初始化执行 , 以下为修改后执行-------------')objProxy.name = 'kobe'objProxy.age = 24
![由浅入深,带你用JavaScript实现响应式原理](http://img.jiangsulong.com/220401/16263445S-9.jpg)
文章插图
5.8.Depend类优化
截止到上面 , 大部分响应式原理已经实现了 , 但是还存在一些小问题需要优化 。
- 优化一:既然currentReactiveFn可以在全局拿到 , 何不在Depend类中就对它进行收集呢 。改造方法addDependFn;
- 优化二:如果一个响应式函数中多次访问了某个属性 , 就都会去到Proxy的get捕获器 , 该响应式函数会被重复收集 , 在调用时就会调用多次 。当属性发生变化后 , 依赖这个属性的响应式函数被调用一次就可以了 。改造reactiveFns , 将数组改成Set , Set可以避免元素重复 , 注意添加元素使用add 。
// 将currentReactiveFn放到Depend之前 , 方便其拿到let currentReactiveFn = nullclass Depend {constructor() {// 用于存放响应式函数this.reactiveFns = new Set()}// 用户添加响应式函数addDependFn() {// 先判断一下currentReactiveFn是否有值if (currentReactiveFn) {this.reactiveFns.add(currentReactiveFn)}}// 用于执行响应式函数notify() {this.reactiveFns.forEach(fn => {fn()})}}
Proxy中就不用去收集响应式函数了 , 直接调用addDependFn即可:// 创建一个Proxyconst objProxy = new Proxy(obj, {get: function(target, key, receiver) {const dep = getDepend(target, key)// 直接调用addDepend方法 , 让它去收集dep.addDependFn()return Reflect.get(target, key, receiver)},set: function(target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)// 根据当前对象target和设置的key , 去获取对应的depconst dep = getDepend(target, key)// 当set捕获器捕获到属性变化时 , 自动去调用notifydep.notify()}})
5.9.多个对象实现响应式前面都只讲了一个对象实现响应式的实现 , 如果有多个对象需要实现可响应式呢?将Proxy封装一下 , 外面套一层函数即可 , 调用该函数 , 返回该对象的代理对象 。
function reactive(obj) {return new Proxy(obj, {get: function(target, key, receiver) {const dep = getDepend(target, key)// 直接调用addDepend方法 , 让它去收集dep.addDependFn()return Reflect.get(target, key, receiver)},set: function(target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)// 根据当前对象target和设置的key , 去获取对应的depconst dep = getDepend(target, key)// 当set捕获器捕获到属性变化时 , 自动去调用notifydep.notify()}})}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 您与这个网站的连接不安全?教你用python轻松解决
- 带状疱疹饮食注意什么
- 给你一个团队,你该怎么带?做好这五点,团队大变样!
- 春天到了,30种野菜辨别图片,带你认识不同的野菜和吃法
- 红烧带鱼烧豆腐的做法
- 吃野菜,带你进入健康地带
- 无神之界|我拼尽全力考上的二本,难道带给我的只能是耻辱吗?女研究生痛哭中发出灵魂之问
- 韩国|开放的“韩国”现状,带你看看真实的韩国
- 黄瓜|恩克减肥34天后回归,体重不降反增加,网友:安全带尽力了
- 大一新生入学必备清单 上大学要带什么东西