JS进阶之Proxy与Object.defineProperty

121 阅读3分钟

   携手创作,共同成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情 >>

一、Proxy 对象代理

Proxy 代理目标对象,换种理解方式便是拦截对象的所有操作,类似:取值,设置值等等。Proxy 代理一个对象,是直接监听整个对象,而非 Object.defineProperty 只是监听对象的 get 与 set。

Proxy(target, handler)

let ProxyObj = Proxy({
	// 代理对象属性
  id: 1,
  name: '‘你好
},{
	// 代理方法
  // obj:代理的对象 prop:被查询的值
  get(obj, prop){
		console.log(`${prop}被查询`)
	},
  // value: 被设置的新值
	set(obj, prop, value){
		console.lgo(`${prop}的值被修改为${value}`)
		return obj[prop] = value
	}
})

Tips:一旦对象被代理之后,对象的 this 就指向了代理对象。

1. Proxy 都能干什么?

  1. get()

get() 方法用于拦截某个属性的读取操作,接受三个参数,依次为目标对象、属性名和 proxy 实例本身(可选属性)。

  1. set()

set() 方法用来拦截某个属性的赋值操作,接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身(可选属性)。

  1. apply()
  • 函数对象也可以作为代理的目标对象。apply() 方法拦截函数的调用、call 和apply 操作。
  • apply() 方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。
  1. construct()

construct 拦截的是 new 操作,接受三个参数:目标对象,构造函数的参数对象,创造实例对象时,new 命令作用的构造函数。

Tips:Proxy 常用的两个方法其实就 get() 和 set() 两个,用来做双向数据绑定和数据响应式。

二、Object.defineProperty 监听对象属性

Object.defineProperty 用于劫持并监听对象属性的某些操作。

Object.defineProperty(target, props, handler)

let obj = {
  username: "",
};
// 三个参数:
// 1. 要操控的对象。
// 2. 要操作的属性。
// 3. 设置具体方法。
// 其原理就是对数据进行劫持,设置数据的每次取值和修改值时都得通过 Object.defineProperty
Object.defineProperty(obj, "username", {
  // 使用 Object.defineProperty 劫持对象的 get 与 set 方法
  // get 方法监听对象的取值操作
  // 也就是说只要输出或者调用了 obj.username 都会触发 get 方法
  // 比如输出:console.log(obj.username) 和
  // 赋值:let a = obj.username 都会触发 get方法
  get: function () {
    console.log("取值操作");
    console.log(obj.username);
  },
  // set 方法监听对象的修改、设置值操作
  // 例:obj.username = xxx 就会触发 set
  set: function (value) {
    console.log("对象值被修改");
    // 动态更新文本值
    document.getElementById("userName").innerText = value;
  },
});

三、两者区别

1. Proxy():

  • 针对整个对象,而不是对象的某个属性
  • 不需要对数组的方法进行重载,省去了众多 hack
  • 不需要遍历对象的每个属性
  • 不需要深层遍历嵌套的对象
  • get 里面递归调用 Proxy 并返回
  • 性能消耗低,但浏览器兼容性低

2. Object.defineProperty():

  • 针针对对象的属性。
  • 不能监听数组的变化。
  • 必须遍历对象的每个属性。
  • 必须深层遍历嵌套的对象。
  • 性能消耗高,但浏览器兼容性高。

3. 总结:

总得来说,Proxy 代理整个对象,而不只是去监听对象的某个属性,且支持对数组的代理,但是兼容性不高,也是 Vue3 才会使用 Proxy 实现双向绑定的一个原因。