恋の歌的logo
分类 归档 标签

JavaScript中的Proxy

发表于2020-06-12
更新于2023-02-13
分类于编程
总字数1.7K
阅读时长 ≈6 分钟

前言 🔗

这个和之前的Reflect有一定的关系,也是Vue3实现的一个很重要的API,学学学。

Proxy 🔗

顾名思义,就是一个代理对象,通过一组配置函数来拦截操作从而使得以源对象得以被扩展。

Proxy是一个构造函数

由两个参数

  • obj 需要代理的对象
  • handler 一组由特定的处理函数组成的对象,如果没有指定,则会使用源对象的默认行为。
javascript
var o = {};

var proxy = new Proxy(o, {});

在Reflect上的方法,Proxy都可以拦截,具体包括

  • getPrototypeOf
  • setPrototypeOf
  • isExtensible
  • preventExtensible
  • getOwnPropertyDescriptor
  • defineProperty
  • has
  • get
  • set
  • deleteProperty
  • ownKeys
  • apply
  • construct

getPrototypeOf() 🔗

读取代理对象的原型时,这个方法就会调用

有一个参数

  • obj 源对象

该函数必须返回一个对象或者null

javascript
var o = {};

var prototype = {
  name: "prototype",
};

var proxy = new Proxy(o, {
  getPrototypeOf(target) {
    return prototype;
  },
});

console.log(Reflect.getPrototypeOf(proxy) === prototype); // 打印true

可以触发该函数的操作包括

  • Object.getPrototypeOf
  • Reflect.getPrototypeOf
  • __proto__
  • Object.prototype.isPrototypeOf
  • instanceof
javascript
var proxy = new Proxy(
  {},
  {
    getPrototypeOf(target) {
      console.log("调用了");
      return Reflect.getPrototypeOf(target);
    },
  },
);

// 打印了5次"调用了"
Object.getPrototypeOf(proxy);
Reflect.getPrototypeOf(proxy);
proxy.__proto__;
Object.prototype.isPrototypeOf(proxy);
proxy instanceof Object;

setPrototypeOf() 🔗

设置代理对象的原型时,这个方法就会调用。

有两个参数

  • obj 源对象
  • prototype 将设置的原型对象

该函数在设置prototype成功返回true,失败返回false

javascript
var proxy = new Proxy(
  {},
  {
    setPrototypeOf(obj, prototype) {
      console.log("设置了prototype");
      return Reflect.setPrototypeOf(obj, prototype);
    },
  },
);

Reflect.setPrototypeOf(proxy, {});

可以触发该函数的操作包括

  • Reflect.setProtytpeOf
  • Object.setPrototypeOf

isExtensible() 🔗

查询代理对象是否可以扩展时,这个方法就会调用。

有一个参数

  • obj 源对象

返回值必须为一个boolean值或者可以转为Boolean的值。

javascript
var proxy = new Proxy(
  {},
  {
    isExtensible(obj) {
      console.log("查询了是否能够扩展");
      return Reflect.isExtensible(obj);
    },
  },
);

Reflect.isExtensible(proxy);

可以触发该函数的操作包括

  • Reflect.isExtensible
  • Object.isExtensible

preventExtensions() 🔗

禁止代理对象扩展时,这个方法就会调用。

有一个参数

  • obj 源对象

返回值必须为一个boolean值或者可以转为boolean的值。

javascript
var proxy = new Proxy(
  {},
  {
    preventExtensions(obj) {
      console.log("禁止了扩展");
      return Reflect.preventExtensions(obj);
    },
  },
);

Reflect.preventExtensions(proxy);

可以触发该函数的操作包括

  • Reflect.preventExtensions
  • Object.preventExtensions

getOwnPropertyDescriptor() 🔗

获取对象属性的属性描述符时,这个方法会调用。

有两个参数

  • obj 源对象
  • propertyKey 需要获取属性描述符的属性名

返回值必须为一个对象或者undefined

javascript
var proxy = new Proxy(
  {
    name: "lwf",
  },
  {
    getOwnPropertyDescriptor(obj, propertyKey) {
      console.log("获取了属性描述符");
      return Reflect.getOwnPropertyDescriptor(obj, propertyKey);
    },
  },
);

Reflect.getOwnPropertyDescriptor(proxy, "name"); // 打印

可以触发该函数的操作包括

  • Reflect.getOwnPropertyDescriptor
  • Object.getOwnPropertyDescriptor

defineProperty() 🔗

在定义代理对象的属性时,这个方法会调用。

有三个参数

  • obj 源对象
  • propertyKey 将设置的属性名
  • descriptor 将设置属性的属性描述符

返回值必须为一个boolean值或者可以转为boolean的值。

javascript
var proxy = new Proxy(
  {},
  {
    defineProperty(obj, propertyKey, descriptor) {
      console.log("设置了对象的属性");
      return Reflect.defineProperty(obj, propertyKey, descriptor);
    },
  },
);

Reflect.defineProperty(proxy, "name", {
  value: "lwf",
}); // 打印

console.log(proxy.name);

可以触发该函数的操作包括

  • Reflect.defineProperty
  • Object.defineProperty
  • proxyObj.propertyKey = value

has() 🔗

在检查属性是否存在对象中时,这个方法会调用。

有两个参数

  • obj 源对象
  • propertyKey 需要检查的属性名

返回值必须为一个boolean值或者可以转为boolean的值。

javascript
var proxy = new Proxy(
  {},
  {
    has(obj, propertyKey) {
      console.log("检查了属性是否存在");
      return propertyKey in obj;
    },
  },
);

"name" in proxy; // 打印

可以触发该函数的操作包括

  • propertyKey in proxy
  • propertyKey in Object.create(proxy)
  • Reflect.has
  • with操作

get() 🔗

在读取代理对象属性时,这个方法会调用。

有三个参数

  • obj 源对象
  • propertyKey 要获取的属性名
  • context 要绑定到getter的上下文,默认为调用对象本身。

返回值可以为任何值。

javascript
var proxy = new Proxy(
  {},
  {
    get(obj, propertyKey, context) {
      console.log("获取了属性值");
      return Reflect.get(obj, propertyKey, context);
    },
  },
);

proxy.name; // 打印

可以触发该函数的操作包括

  • proxy[propertyKey]或者proxy.propertyKey
  • Object.create(proxy)[propertyKey]或者Object.create(proxy).propertyKey
  • Reflect.get

set() 🔗

在设置代理对象属性值时,这个方法会调用。

有四个参数

  • obj 源对象
  • propertyKey 设置的属性名
  • value 设置的属性值
  • context 要绑定到setter的上下文,默认为调用对象本身。

TIPS: 对于getset,其中context参数的传入通常是proxy对象本身,但是对于处于原型链上的proxy,传入的就不是proxy对象了,而是触发操作的那个对象,比如,obj.propertykey = valueobj不是代理对象,但是obj的原型链上存在代理对象proxy,那传入context参数的就是obj而不是proxy了。而对于通过Reflect.set操作触发,context参数就跟set函数指定的一样了。

返回值必须为一个boolean值或者可以转为boolean的值。

javascript
var proxy = new Proxy(
  {},
  {
    set(obj, propertyKey, value, context) {
      console.log("调用了设置值");
      return Reflect.set(obj, propertyKey, value, context);
    },
  },
);

proxy.name = "lwf"; // 打印

可以触发该函数的操作包括

  • proxy[propertyKey] = value或者proxy.propertyKey = value
  • Object.create(proxy)[propertyKey] = value或者Object.create(proxy).propertyKey = value
  • Reflect.set

deteleProperty() 🔗

在删除对象的属性时,这个方法会调用

有两个参数

  • obj 源对象
  • propertyKey 将删除的属性名

返回值必须为一个boolean值或者可以转为boolean的值。

javascript
var proxy = new Proxy(
  {},
  {
    deleteProperty(obj, propertyKey) {
      console.log("删除了属性");
      return Reflect.deleteProperty(obj, propertyKey);
    },
  },
);

delete proxy.name; // 打印

可以触发该函数的操作包括

  • delete proxy[propertyKey]或者delete proxy.propertyKey
  • Reflect.deleteProperty

ownKeys() 🔗

获取代理对象属性名组成的数组时,这个方法会调用

有一个参数

  • obj 源对象

返回值必须为一个可枚举的对象。

javascript
var proxy = new Proxy(
  {},
  {
    ownKeys(obj) {
      console.log("获取了属性名组成的数组");
      return Reflect.ownKeys(obj);
    },
  },
);

Reflect.ownKeys(proxy); // 打印

可以触发该函数的操作包括

  • Object.getOwnPropertyNames
  • Object.getOwnPropertySymbols
  • Reflect.ownKeys
  • Object.keys

apply() 🔗

在对代理对象进行函数调用时,这个方法会调用

有一个参数

  • fn 源函数
  • context 执行函数的上下文
  • args 参数的数组

返回值可以为任意值。

javascript
function fn(msg) {
  console.log(msg);
}

var proxy = new Proxy(fn, {
  apply(fn, context, args) {
    console.log("执行了函数");
    return fn.apply(context, args);
  },
});

proxy("hello world"); // 打印

可以触发该函数的操作包括

  • proxy(arg1,arg2,...,argN)
  • Function.prototype.apply或者Function.prototype.call
  • Reflect.apply

construct() 🔗

以代理对象为构造器或者原型上存在代理对象的构造器生成对象时,这个方法会调用

有三个参数

  • obj 源对象(可以被new
  • args 参数数组
  • newTarget 被调用的构造函数

返回值必须为一个对象

javascript
function fn(name) {
  this.name = name;
}

var proxy = new Proxy(fn, {
  construct(obj, args, newTarget) {
    console.log("构造了新对象");
    return Reflect.construct(obj, args);
  },
});

var p = new proxy("lwf"); // 打印

console.log(p.name); // 打印 "lwf"

可以触发该函数的操作包括

  • new proxy(arg1,arg2,...,argN)
  • Reflect.construct

后记 🔗

学完感觉我能看懂vue3源码了

哦呐该,如果没有评论的话,瓦达西...