由浅入深,带你用JavaScript实现响应式原理( 四 )

  • receiver的作用就在这里 , 把原对象中this改成其代理对象 , 同理age被设置也是一样的 , 访问和设置信息都需要被打印两次;
  • // 假设obj的age为私有属性 , 需要通过getter和setter来访问和设置const obj = {name: 'curry',_age: 30,get age() {return this._age},set age(newValue) {this._age = newValue}}const objProxy = new Proxy(obj, {get: function(target, key, receiver) {console.log(`obj对象的${key}属性被访问啦!`)return Reflect.get(target, key, receiver)},set: function(target, key, newValue, receiver) {console.log(`obj对象的${key}属性被设置啦!`)Reflect.set(target, key, newValue, receiver)}})// 设置:objProxy.name = 'kobe'objProxy.age = 24// 访问:console.log(objProxy.name)console.log(objProxy.age)再来看一下打印结果:
    由浅入深,带你用JavaScript实现响应式原理

    文章插图
     
    也可以打印receiver , 在浏览器中进行查看 , 其实就是这里的objProxy:
    由浅入深,带你用JavaScript实现响应式原理

    文章插图
     
    5.响应式原理的实现5.1.什么是响应式呢?
    当某个变量值发生变化时 , 会自动去执行某一些代码 。如下代码 , 当变量num发生变化时 , 对num有所依赖的代码可以自动执行 。
    let num = 30console.log(num) // 当num方式变化时 , 这段代码能自动执行console.log(num * 30) // 当num方式变化时 , 这段代码能自动执行num = 1
    • 像上面这一种自动响应数据变化的代码机制 , 就称之为响应式;
    • 在开发中 , 一般都是监听某一个对象中属性的变化 , 然后自动去执行某一些代码块 , 而这些代码块一般都存放在一个函数中 , 因为函数可以方便我们再次执行这些代码 , 只需再次调用函数即可;
    5.2.收集响应式函数的实现
    在响应式中 , 需要执行的代码可能不止一行 , 而且也不可能一行行去执行 , 所以可以将这些代码放到一个函数中 , 当数据发生变化 , 自动去执行某一个函数 。但是在开发中有那么多函数 , 怎么判断哪些函数需要响应式?哪些又不需要呢?
    • 封装一个watchFn的函数 , 将需要响应式的函数传入;
    • watchFn的主要职责就是将这些需要响应式的函数收集起来 , 存放到一个数组reactiveFns中;
    const obj = {name: 'curry',age: 30}// 定义一个存放响应式函数的数组const reactiveFns = []// 封装一个用于收集响应式函数的函数function watchFn(fn) {reactiveFns.push(fn)}watchFn(function() {let newName = obj.nameconsole.log(newName)console.log('1:' + obj.name)})watchFn(function() {console.log('2:' + obj.name)})obj.name = 'kobe'// 当obj中的属性值发送变化时 , 遍历执行那些收集的响应式函数reactiveFns.forEach(fn => {fn()})
    由浅入深,带你用JavaScript实现响应式原理

    文章插图
     
    5.3.收集响应式函数的优化
    上面实现的收集响应式函数 , 目前是存放到一个数组中来保存的 , 而且只是对name属性的的依赖进行了收集 , 如果age属性也需要收集 , 不可能都存放到一个数组里面 , 而且属性值改变后 , 还需要通过手动去遍历调用 , 显而易见是很麻烦的 , 下面做一些优化 。
    • 封装一个类 , 专门用于收集这些响应式函数;
    • 类中添加一个notify的方法 , 用于遍历调用这些响应式函数;
    • 对于不同的属性 , 就分别去实例化这个类 , 那么每个属性就可以对应一个对象 , 并且对象中有一个存放它的响应式数组的属性reactiveFns;
    class Depend {constructor() {// 用于存放响应式函数this.reactiveFns = []}// 用户添加响应式函数addDependFn(fn) {this.reactiveFns.push(fn)}// 用于执行响应式函数notify() {this.reactiveFns.forEach(fn => {fn()})}}const obj = {name: 'curry',age: 30}const dep = new Depend()// 在watchFn中使用dep的addDependFn来收集function watchFn(fn) {dep.addDependFn(fn)}watchFn(function() {let newName = obj.nameconsole.log(newName)console.log('1:' + obj.name)})watchFn(function() {console.log('2:' + obj.name)})obj.name = 'kobe'// name属性发生改变 , 直接调用notifydep.notify()


    推荐阅读