Object.defineProperty和Proxy分别实现响应式原理的简单示例

  1. 利用Object.defineProperty+递归 实现响应式原理

    // 监听数组, Object.defineProperty做不到, 所以需要重写数组方法!!!
    // 重新定义数组原型
    const oldArrProperty = Array.prototype
    // 创建新对象, 原型指向oldArrProperty, 再扩展新的方法不会影响Array原型
    // 使用Object.create是为了不污染全局的原型方法
    const arrProto = Object.create(oldArrProperty);
    ['push', 'pop', 'shift', 'unshift', 'splice'].forEach(element => {
    arrProto[element] = function () {
    updateView(); // 触发视图更新
    oldArrProperty[element].call(this, ...arguments);
    }
    });

    // 监听对象属性
    function observer(target) {
    if (typeof target !== 'object' || target === null) {
    // 不是对象或数组
    return target;
    }

    复制代码
     if (Array.isArray(target)) {
         target.__proto__ = arrProto
     }
    
     // 重新定义各个属性(for in 也可以遍历数组)
     for (let key in target) {
         defineReactive(target, key, target[key])
     }

    }

    // 重新定义属性, 监听起来
    function defineReactive(target, key, value) {
    // 深度监听(深层对象):
    observer(value);

    复制代码
     // 核心API
     Object.defineProperty(target, key, {
         get() {
             return value
         },
         set(newVal) {
             if (newVal !== value) {
                 // 设置新值的时候也要深度监听(深层对象):
                 observer(newVal)
                 // 设置新值
                 // 注意, value一直闭包中, 此处设置完之后, 再get时也是会获取最新的值
                 value = newVal
    
                 // 触发更新视图
                 updateView();
             }
         }
     })

    }

    // 触发更新视图
    function updateView() {
    console.log('视图更新')
    }
    const data = {
    name: 'zhangsan',
    age: 20,
    info: {
    city: '北京', // 需要深度监听
    },
    nums: []
    }

    // 监听数据
    observer(data)

    //测试
    // data.name='sdhewifewfewfewf'
    // data.age = 21
    // data.info.city = 'shanghai'
    // data.x = '100';//新增属性, 监听不到--所以有V
    // delete data.name; // 删除属性, 监听不到 --所以有Vue.delete
    data.nums.push({ a: 1 }); // 监听数组
    data.nums[0].a = 222
    // console.log(data.name, data.age, data.info.city, data.nums)

    // console.log(arrProto)
    // console.log(arrProto.proto.push)
    // console.log(arrProto.proto === oldArrProperty)
    // console.log(arrProto.prototype.constructor)

测试: 每次修改数据, 查看控制台有没有随着数据变化打印"视图更新"

  1. 使用Proxy和Reflect实现响应式原理, 如果有深层对象和数组也需要递归

    var data = {
    name: 'zhangsan',
    age: 20,
    list: [1, 2],
    obj: { a: 1, b: 2, c: 2, city: {
    name: '北京'
    }}
    }

    function reactive(data) {
    const config = {
    // receiver其实指的就是Proxy
    get(target, key, receiver) {
    // Reflect.ownKeys可以获取不是原型上的属性
    const ownKeys = Reflect.ownKeys(target);
    // 只处理本身(非原型的)属性
    // if(ownKeys.includes(key)) {
    // console.log('get:', key, '本身(非原型的)属性');
    // }
    const result = Reflect.get(target, key, receiver);
    // 如果对象有深层的对象或者数组, 需要递归
    if(Array.isArray(result) || Object.prototype.toString.call(result) === '[object Object]') {
    return reactive(result);
    }
    console.log('get:', key, target[key], result)
    return result;
    },
    set(target, key, val, receiver) {
    // 重复的数据不处理
    const oldVal = target[key]
    if(val === oldVal) {
    return true
    }

    复制代码
             const ownKeys = Reflect.ownKeys(target);
             // 只处理本身(非原型的)属性
             if(ownKeys.includes(key)) { // push方法就不执行了
                 console.log('get:', key, '已有的key');
             }
             else {
                 console.log('get:', key, '新增的key');
             }
    
             const result = Reflect.set(target, key, val, receiver);
             console.log('set:', key, val, result)
             // 触发更新视图
             updateView();
             return result;
         },
         deleteProperty(target, key) {
             const result = Reflect.deleteProperty(target, key);
             console.log('deleteProperty:', key, result)
             // 触发更新视图
             updateView();
             return result;
         },
     }
     const proxyData = new Proxy(data, config);
     return proxyData

    }

    // 触发更新视图
    function updateView() {
    console.log('视图更新')
    }

    const proxyData = reactive(data)
    proxyData.list.push('13')
    proxyData.obj.a = 999
    proxyData.obj.b = undefined
    proxyData.obj.city.name = 'shanghai'
    Reflect.deleteProperty(proxyData.obj, 'c')
    proxyData.obj.d = 'ddddd'

测试: 每次修改数据, 查看控制台有没有随着数据变化打印"视图更新"

相关推荐
掘金安东尼12 小时前
CSS 颜色混乱实验
前端·javascript·github
Tiger_shl12 小时前
【.Net技术栈梳理】08-控制反转(IoC)与依赖注入(DI)
开发语言·.net·.netcore
Tiger_shl12 小时前
【.Net技术栈梳理】10-.NET Core 程序的执行
开发语言·.net·.netcore
Mintopia13 小时前
轻量化AIGC模型在移动端Web应用的适配技术
前端·javascript·aigc
Mintopia13 小时前
Next.js CI/CD 基础(GitHub Actions)
前端·javascript·next.js
薄荷撞~可乐13 小时前
C#高并发与并行理解处理
开发语言·c#
孤廖13 小时前
【算法磨剑:用 C++ 思考的艺术・Dijkstra 实战】弱化版 vs 标准版模板,洛谷 P3371/P4779 双题精讲
java·开发语言·c++·程序人生·算法·贪心算法·启发式算法
sali-tec13 小时前
C# 基于halcon的视觉工作流-章33-矩状测量
开发语言·人工智能·算法·计算机视觉·c#
酷炫码神14 小时前
第 2 篇:Java 入门实战(JDK8 版)—— 编写第一个 Java 程序,理解基础运行逻辑
java·开发语言·策略模式
像风一样自由202014 小时前
Go语言详细指南:特点、应用场景与开发工具
开发语言·后端·golang