Object.defineProperty、Proxy、Reflect、vue2/vue3的响应式原理

2023-11-02

Object.defineProperty()

const obj = {
        name: '小黑',
        age: 18
    }
    Object.keys(obj).forEach(key => {
        let value = obj[key]
        Object.defineProperty(obj, key, {
            get() {
                console.log(`obj.${key}被访问`);
                return value
            },
            set(newValue) {
                console.log(`obj.${key}被修改`);
                value = newValue
            },
        })
    })
    obj.name = "小白"
    console.log(obj.name);
    obj.age = 12
    console.log(obj.age);

注意:它没有办法监听更加丰富的操作,只能监听获取和修改

Proxy的基本使用

const obj = {
        name: '小黑',
        age: 18
    }
    // Proxy 参数一:需要代理的对象  参数二:捕获器  例如捕获增加,删除等
    // 可以修改原对象
    const objProxy = new Proxy(obj, {
        // target 原对象 key对象的key
        get(target, key) {
            console.log("属性被访问");
            return target[key]
        },
        // newValue 新值
        set(target, key, newValue) {
            console.log("属性被修改");
            target[key] = newValue
        }
    })
    objProxy.name = '小白'
    objProxy.age = 12
    console.log(objProxy.name);
    console.log(objProxy.age);
    // console.log(obj.name);
    // console.log(obj.age);
 

proxy的所有捕获器

  • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']

  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。

  • has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。

  • deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。

  • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。

  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。

  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。

  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。

  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。

  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。

  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。

  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)

  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

Reflect

Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。

Reflect和Proxy结合使用

    const obj = {
        name: '小黑',
        age: 18
    }
    const objProxy = new Proxy(obj, {
        get(target, key, receiver) {
            console.log("属性被访问");
            // return target[key]
            return Reflect.get(target, key)
        },
        set(target, key, newValue, receiver) {
            console.log("属性被修改");
            Reflect.set(target, key, newValue)
        }
    })
    objProxy.name = '小白'
    objProxy.age = 12
    console.log(objProxy.name);
    console.log(objProxy.age);

Proxy主要就是不希望修改原本的对象,Reflect刚好解决这个问题

Reflect的方法有返回值,可以返回是否设置成功的布尔值

Receiver的作用

const obj = {
    _name: "小黑", //    下划线是常用标记,表示是内部属性,只能通过对象的方法进行读写
    get name() {
        return this._name
    },
    set name(newValue) {
        this._name = newValue
    }
}
const objProxy = new Proxy(obj, {
    get(target, key, receiver) {
        // receiver 是代理对象
        console.log(key);
        return Reflect.get(target, key, receiver)  // receiver改变target对象get方法的this
    },
    set(target, key, newValue, receiver) {
        console.log(key);
        Reflect.set(target, key, newValue, receiver)
    }
})
objProxy.name = "小白"
console.log(objProxy.name);

Reflect.construct(target,argumentsList,newTarget)

function Student(name, age) {
    this.name = name
    this.age = age
}
function Teacher() { }
// 执行Student的内容,创建Teacher对象
const teacher = Reflect.construct(Student, ['小黑', 18], Teacher)
console.log(teacher);
console.log(teacher.__proto__ === Teacher.prototype);

Proxy响应式

// 收集依赖的响应式函数
let activeReactiveFn = null
class Depend {
    constructor() {
        // 依赖收集
        this.reactiveFns = new Set()
    }
    // addDepend(reactiveFns) {
    //     this.reactiveFns.add(reactiveFns)
    // }
​
    depend() {
        if (activeReactiveFn) {
            this.reactiveFns.add(activeReactiveFn)
        }
    }
​
    notify() {
        this.reactiveFns.forEach(fn => {
            fn()
        })
    }
}
​
// 响应式函数
const depend = new Depend()
function watchFn(fn) {
    activeReactiveFn = fn
    fn()
    activeReactiveFn = null
}
​
// 封装获取depend的函数
const targetMap = new WeakMap()
function getDepend(target, key) {
    // 根据target对象获取map的过程
    let map = targetMap.get(target)
    if (!map) {
        map = new Map()
        targetMap.set(target, map)
    }
    // 根据key获取depend对象
    let depend = map.get(key)
    if (!depend) {
        depend = new Depend()
        map.set(key, depend)
    }
    return depend
}
​
// 响应式对象的封装
function reactive(obj) {
    return new Proxy(obj, {
        get(target, key, receiver) {
            // 根据target, key获取对应的depend
            const depend = getDepend(target, key)
            // 添加函数
            depend.depend()
            return Reflect.get(target, key, receiver)  // receiver改变target对象get方法的this
        },
        set(target, key, newValue, receiver) {
            Reflect.set(target, key, newValue, receiver)
            // 根据target, key获取对应的depend
            const depend = getDepend(target, key)
            depend.notify()
        }
    })
}
​
​
// 对象的响应式
const objProxy = reactive({
    name: '小黑',
    age: 18
})
const infoProxy = reactive({
    address: '武汉',
})
watchFn(function () {
    console.log(objProxy.name, '---------------');
    console.log(objProxy.name, '+++++++++++++++');
})
watchFn(function () {
    console.log(infoProxy.address, '---------------');
    console.log(infoProxy.address, '+++++++++++++++');
})
objProxy.name = '小白'
infoProxy.address = '深圳'

Object.defineProperty响应式

// 收集依赖的响应式函数
let activeReactiveFn = null
class Depend {
    constructor() {
        // 依赖收集
        this.reactiveFns = new Set()
    }
    depend() {
        if (activeReactiveFn) {
            this.reactiveFns.add(activeReactiveFn)
        }
    }
    notify() {
        this.reactiveFns.forEach(fn => {
            fn()
        })
    }
}
​
// 响应式函数
const depend = new Depend()
function watchFn(fn) {
    activeReactiveFn = fn
    fn()
    activeReactiveFn = null
}
​
// 封装获取depend的函数
const targetMap = new WeakMap()
function getDepend(target, key) {
    // 根据target对象获取map的过程
    let map = targetMap.get(target)
    if (!map) {
        map = new Map()
        targetMap.set(target, map)
    }
    // 根据key获取depend对象
    let depend = map.get(key)
    if (!depend) {
        depend = new Depend()
        map.set(key, depend)
    }
    return depend
}
​
// 响应式对象的封装
function reactive(obj) {
    Object.keys(obj).forEach(key => {
        let value = obj[key]
        Object.defineProperty(obj,key, {
            get() {
                const depend = getDepend(obj, key)
                depend.depend()
                return value
            },
            set(newvalue) {
                value = newvalue
                const depend = getDepend(obj, key)
                depend.notify()
            }
        })
    })
    return obj
}
​
​
// 对象的响应式
const objProxy = reactive({
    name: '小黑',
    age: 18
})
const infoProxy = reactive({
    address: '武汉',
})
watchFn(function () {
    console.log(objProxy.name, '---------------');
    console.log(objProxy.name, '+++++++++++++++');
})
watchFn(function () {
    console.log(infoProxy.address, '---------------');
    console.log(infoProxy.address, '+++++++++++++++');
})
objProxy.name = '小白'
infoProxy.address = '深圳'

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Object.defineProperty、Proxy、Reflect、vue2/vue3的响应式原理 的相关文章

  • Javascript 函数查找数字的倍数

    创建一个名为的函数multiplesOf 它将接受两个参数 第一个参数是数字数组 第二个参数是数字 该函数应返回一个新数组 该数组由参数数组中的每个数字组成 该数字是参数数字的倍数 So multiplesOf 5 6 7 8 9 10 3
  • 在 contenteditable div 中选择范围

    我有一个contenteditablediv 和其中的一些段落 这是我的代码 div style border solid 1px black width 300px height 300px div Hello world div div
  • 渲染货币和符号并与来自不同单元格的数据相结合

    我正在使用最新的 jQuery DataTables v1 10 7 我正在尝试将数字解析为以下格式 239 90 USD 我可以使用此命令使货币正常工作 columns data Price render fn dataTable ren
  • 以编程方式填写reactjs表单

    我正在编写一个用户脚本 但无法填写由reactjs制作的表单 我的代码 document querySelector id username value email protected cdn cgi l email protection
  • Three.js:缩放几何图形后错误的 BoundingBox

    在我的场景中 我有一个简单的立方体 var test new THREE Mesh new THREE CubeGeometry 10 10 10 new THREE MeshBasicMaterial scene add test 该立方
  • 如何使用javascript将大图像转换为十六进制?

    如果我尝试将图像转换为十六进制 无论我使用哪个函数 我都会收到此错误消息 该图像的大小为 7 MB 19812 毫秒 清理 1401 2 1455 0 gt 1401 2 1455 0 MB 9 9 0 ms 自上次 GC 以来 8 3 m
  • 如何格式化 Highcharts 的 (x,y) 对数据的日期时间

    我的序列化方法会产生如下所示的日期时间字符串 2014 07 09T12 30 41Z 为什么下面的代码不起作用 function container highcharts xAxis type datetime series data x
  • 可以在初始 DOM 解析期间/之前修改 DOM 吗?

    是否可以在初始 DOM 解析期间或之前修改 DOM 或者我是否必须等到 DOM 被解析和构建之后才能与其交互 更具体地说 是否有可能阻止 DOM 中的脚本元素使用用户脚本 内容脚本或 Chrome 或 Firefox 中的类似脚本运行 在解
  • 在打字稿中导入 json

    我是 typescript 的新手 在我的项目中 我们使用 typescript2 在我的要求之一中 我需要导入 json 文件 所以我创建了 d ts 文件如下 test d ts declare module json const va
  • React-Redux:state.setIn() 和 state.set() 有什么区别?

    我见过使用setIn and set 在一些react redux代码中 state setIn state set 我在这里找到了一些文档https facebook github io immutable js https facebo
  • 有没有办法使用 Rspec/Capybara/Selenium 将 javascript console.errors 打印到终端?

    当我运行 rspec 时 是否可以让 capybara selenium 向 rspec 报告任何 javascript console errors 和其他异常 我有一大堆测试失败 但当我手动测试它时 我的应用程序正在运行 如果不知道仅在
  • 防止 iOS 键盘在 cordova 3.5 中滚动页面

    我正在使用 Cordova 3 5 和 jQuery mobile 构建 iOS 应用程序 我在大部分应用程序中禁用了滚动功能 但是 当我选择输入字段时 iOS 键盘会打开并向上滚动页面 我不想要这个功能 由于输入足够高 键盘不会覆盖它 我
  • 调整图像大小并将画布旋转 90 度

    这里有很多关于在 js 上使用画布旋转图像的主题 我阅读了其中的大部分内容 但无法找到解决我的问题的方法 我正在接收任何分辨率的图像 来自上传组件 我将其大小调整为 1024x768 如下所示 var canvas document cre
  • 使用 CSS 或 Javascript 填充动画

    我只是想知道是否可以使用 CSS 或 javascript 创建填充动画 基本上我想创建一个填充动画 如下图所示 http i40 tinypic com eit6ia png http i40 tinypic com eit6ia png
  • 在 HTML5 画布中,如何用我选择的背景遮盖图像?

    我试图用画布来实现这一点 globalCompositeOperation 但没有运气 所以我在这里问 这里有类似的问题 但我没有在其中找到我的案例 我的画布区域中有图层 从下到上的绘制顺序 画布底座填充纯白色 fff 用fillRect
  • Javascript split 不是一个函数

    嘿朋友们 我正在使用 javascript sdk 通过 jQuery facebook 多朋友选择器在用户朋友墙上发布信息 但是我收到此错误friendId split 不是函数 这是我的代码 function recommendToFr
  • 检查 jQuery 1.7 中是否存在基于文本的选择选项

    所以我有以下 HTML 片段
  • 在移动设备上滚动

    这个问题更多的是一个建议研究 我确实希望它对其他人有帮助 并且它不会关闭 因为我不太确定在哪里寻求有关此事的建议 在过去的 6 个月里 我一直在进行移动开发 我有机会处理各种设备上的各种情况和错误 最麻烦的是滚动问题 当涉及到在网站的多个区
  • Vue 和 Vuex:处理依赖的计算属性

    我的应用程序是一个使用 Vuex 在 Vue 中构建的精简电子表格 关键组件是TableCollection Table and Row The TableCollection有一个包含多个的数组Table对象 每个Table有一个包含多个
  • 如何使用 crypto-js 解密 AES ECB

    我正在尝试将加密数据从 flash 客户端 发送到服务器端的 javascript 在 asp 中作为 jscript 运行 有几个 javascript Aes 库 但它们实际上没有文档记录 我正在尝试使用 crypto js 但无法让代

随机推荐