Skip to content

记录一下 Object.defineProperty 与 Proxy 的区别

前言

大家好,我是你们的友好邻居 Talon ,昨天刷短视频偶然看到,直播老师问了一个同学 Object.definePropertyProxy 的区别,以及 Object.defineProperty 为什么不能监听数组长度的变化,今天我们就来记录一下。

一、Object.defineProperty

  • 用途:用于直接在对象上定义一个新属性或修改现有属性的特性,对目标对象进行数据劫持。
  • 影响范围: 作用于单个属性。
  • 兼容性: 支持的浏览器版本较旧,较早引入到 JavaScript 中。

举个栗子

js
let person = {}

Object.defineProperty(person, 'age', {
    value: 18,
    writable: false, // 当前属性是否可写 默认 false
    enumerable: true, // 是否可以通过 for-in 循环返回 默认 true
    configurable: true, // 是否可以通过 delete 删除并重新定义 默认 true
})

console.log(person.age) // 输出 18
let person = {}

Object.defineProperty(person, 'age', {
    value: 18,
    writable: false, // 当前属性是否可写 默认 false
    enumerable: true, // 是否可以通过 for-in 循环返回 默认 true
    configurable: true, // 是否可以通过 delete 删除并重新定义 默认 true
})

console.log(person.age) // 输出 18

set() 函数和 get() 函数

js
let book = {
    year_: 2017, // year_中的下划线常用来表示该属性,并不希望在对象方法的外部被访问
    edition: 1
}
Object.defineProperty(book, 'year', {
    /**
     * 获取函数,在读取属性时调用。默认值为 undefined。
     * @returns {number}
     */
    get: function () {
        return this.year_
    },
    /**
     * 设置函数,在写入属性时调用。默认值为 undefined。
     * 当 year 属性被改变时调用,即 newValue 等于你设置的值。
     * @param {number} newValue
     */
    set: function (newValue) {
        if (newValue > 2017) {
            this.year_ = newValue
            this.edition += newValue - this.year_
        }
    }
})
book.year = 2018
console.log(book.edition) // 输出 2
let book = {
    year_: 2017, // year_中的下划线常用来表示该属性,并不希望在对象方法的外部被访问
    edition: 1
}
Object.defineProperty(book, 'year', {
    /**
     * 获取函数,在读取属性时调用。默认值为 undefined。
     * @returns {number}
     */
    get: function () {
        return this.year_
    },
    /**
     * 设置函数,在写入属性时调用。默认值为 undefined。
     * 当 year 属性被改变时调用,即 newValue 等于你设置的值。
     * @param {number} newValue
     */
    set: function (newValue) {
        if (newValue > 2017) {
            this.year_ = newValue
            this.edition += newValue - this.year_
        }
    }
})
book.year = 2018
console.log(book.edition) // 输出 2

Object.defineProperty 为什么不能监听数组长度的变化?

因为数组的一些操作,比如直接通过索引修改元素或使用数组的 push、pop、splice 等方法,会绕过 Object.defineProperty 的 setter 函数,对于重新赋值length的数组,不会新增索引,因为不清楚新增的索引数量,所以监听不到。 为什么Object.defineProperty不能检测到数组长度的变化

二、Proxy

  • 用途: 提供一种创建代理对象,以便自定义目标对象的行为的机制。可以拦截目标对象上的多个操作。
  • 影响范围: 能够拦截对象的多个操作,包括读取、写入、删除等。
  • 兼容性: 较新的特性,需要较新版本的 JavaScript 引擎支持。

举个栗子

js
const handler = {
    /**
     * get 函数是一个捕获器,可以理解为一个基本操作的拦截器,当读取代理对象的属性时会触发此操作
     * @param target 被代理的对象即目标对象
     * @param prop 目标对象的属性
     * @param receiver 代理对象
     * @returns {any} 返回 将要被读取属性的属性值
     */
    get(target, prop, receiver) {
        console.log(`读取属性:${prop}`, receiver);
        return target[prop]
    },

    /**
     * set 函数是一个捕获器,可以理解为一个基本操作的拦截器,当设置代理对象的属性时会触发此操作
     * @param target 被代理的对象即目标对象
     * @param prop 目标对象的属性
     * @param value 要赋给属性的值
     * @param receiver 接收最初赋值的对象
     * @returns {boolean} 返回 true 表示成功;返回 false 表示失败,严格模式下会抛出 TypeError。
     */
    set(target, prop, value, receiver) {
        console.log(`设置属性 ${prop} 值为 ${value}`, receiver);
        target[prop] = value
        return true
    }
}

const proxy = new Proxy({}, handler)
proxy.num = 1 // 触发 set操作 设置属性
console.log(proxy.num); // 触发 get操作 读取属性
const handler = {
    /**
     * get 函数是一个捕获器,可以理解为一个基本操作的拦截器,当读取代理对象的属性时会触发此操作
     * @param target 被代理的对象即目标对象
     * @param prop 目标对象的属性
     * @param receiver 代理对象
     * @returns {any} 返回 将要被读取属性的属性值
     */
    get(target, prop, receiver) {
        console.log(`读取属性:${prop}`, receiver);
        return target[prop]
    },

    /**
     * set 函数是一个捕获器,可以理解为一个基本操作的拦截器,当设置代理对象的属性时会触发此操作
     * @param target 被代理的对象即目标对象
     * @param prop 目标对象的属性
     * @param value 要赋给属性的值
     * @param receiver 接收最初赋值的对象
     * @returns {boolean} 返回 true 表示成功;返回 false 表示失败,严格模式下会抛出 TypeError。
     */
    set(target, prop, value, receiver) {
        console.log(`设置属性 ${prop} 值为 ${value}`, receiver);
        target[prop] = value
        return true
    }
}

const proxy = new Proxy({}, handler)
proxy.num = 1 // 触发 set操作 设置属性
console.log(proxy.num); // 触发 get操作 读取属性

三、总结

  • Object.defineProperty 用于操作单个属性,而 Proxy 可以拦截更多的操作,并作用于整个对象。
  • Proxy 提供了更灵活和强大的拦截机制,可以拦截目标对象上的多种操作,而 Object.defineProperty 的能力相对较为有限。
  • Proxy 是一种相对较新的特性,可能在一些较老的 JavaScript 环境中不被支持。

最后

好了,今天的问题就到这里啦,希望对你们有帮助,祝大家工作顺利,生活愉快! 读者有什么更好的方式想法,欢迎留言评论,一起学习一起进步!