JavaScript的es6新增Proxy

103 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

之前学了vue3课程(vue3的响应式原理使用的就是Proxy),课程中老师经常提到proxy、响应式原理等,感觉很难很高深。现在学了,才真正了解。

一、proxy是什么?

MDN: proxy对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

换句话说,proxy可以监听一个对象的相关操作,比如对象添加、删除、修改属性等。通过捕获器监听对象的改变。

proxy代理是es6新增的方法。

二、es6之前监听对象的改变

在es6直接如果想要监听对象的改变,需要使用Object.defineProperty()进行监听。

const obj = {name:'wgy',age:18}
Object.defineProperty(obj,'name',{
    get:function(){
        console.log('name属性被访问了')
    },
    set:function(newVal){
        console.log('name属性被设置了',newVal)
    }
})
console.log(obj.name)
obj.name = 'kobo'
Object.defineProperty()的缺点
  • 比如新增属性、删除属性,defineProperty监听不到
  • defineProperty的设计初衷是定义普通的属性,不是为了去监听一个对象中的所有属性

三、proxy如何监听

const obj = { name: "wgy", age: 18 };
 
const objProxy = new Proxy(obj, {
  // 获取值时的捕获器  target-obj,key-name
  get(target, key, receiver) {
    console.log("值被获取了", target, key);
    return target[key];
  },
  // 设置值时的捕获器
  set(target, key, newValue, receiver) {
    console.log("值被设置了", newValue);
    target[key] = newValue;
  },
  // 监听in的捕获器
  has(target, key) {
    console.log("对象执行了in操作");
    return key in target;
  },
  // 监听delete的捕获器
  deleteProperty(target, key) {
    console.log("对象执行了delete操作");
    delete target[key];
  },
});
 
console.log(objProxy.name);
objProxy.name = "3333";
console.log(obj);
console.log("name" in objProxy);
delete objProxy.name;
proxy捕获器
  • getPrototype()

    • Object.getPrototypeof() 获取对象的原型
  • setPrototype()

    • Object.setPrototypeof() 设置对象的原型
  • isExtensible()

    • Object.isExtensible() 判断对象是否可以添加属性
  • preventExtensions()

    • Object.preventExtensions() 阻止对象添加属性
  • getOwnPrototyDescriptor()

    • Object.getOwnPrototyDescriptor() 获取对象的属性描述符
  • defineProperty()

    • Object.defineProperty() 给对象添加属性描述符
  • ownKeys()

    • Object.getOwnPrototyNames()

    • Object.getOwnPrototySymbols()

  • apply() 用于函数对象

    • 监听函数调用操作
  • construct 用于函数对象

    • 监听new 操作符
//apply()和construct()捕获器用于函数对象,使用方法
function foo() {
  console.log(foo);
}
const fooProxy = new Proxy(foo, {
  apply(target, thisAry, argArray) {
    console.log("函数被调用了", thisAry, argArray);
    target.apply(thisAry, argArray);
  },
  construct(target, argArray, newTarget) {
    console.log("函数使用new");
    return new target(...argArray);
  },
});
fooProxy();
fooProxy.apply({}, [1, 4, 6]);
new fooProxy();

Proxy的receiver参数的使用

修改this指向,使代理有意义。

const obj = {
  _name: "www",
  get name() {
    console.log(this);
    return this._name; //this指向obj
  },
  set name(newVal) {
    this._name = newVal;
  },
};
 
const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    // receiver是代理对象objProxy
    console.log("属性被获取了", key, receiver === objProxy);
    return Reflect.get(target, key, receiver);
    // 传入receiver,会把this改为代理对象receiver
  },
  set(target, key, newVal, receiver) {
    console.log("属性被设置了");
    Reflect.set(target, key, newVal, receiver);
  },
});
 
console.log(objProxy.name); //访问的是obj的get name
objProxy.name = "333333";

写在最后:vue3的响应式原理就是通过proxy来实现的,如果要学习vue3可以先学习一下proxy的。