记录一下 Object.defineProperty 与 Proxy 的区别
前言
大家好,我是你们的友好邻居 Talon ,昨天刷短视频偶然看到,直播老师问了一个同学 Object.defineProperty
与 Proxy
的区别,以及 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 环境中不被支持。
最后
好了,今天的问题就到这里啦,希望对你们有帮助,祝大家工作顺利,生活愉快! 读者有什么更好的方式想法,欢迎留言评论,一起学习一起进步!