JS的内置对象Proxy

126 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

Proxy对象

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

  1. target:要使用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
  2. handler:要使用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。handler对象是一个容纳一批特定属性的占位符对象。它包含有Proxy的各个捕获器(trap)。

handler对象的方法:

  1. handler.getPrototypeOf()是Object.getPrototypeOf方法的捕捉器。getPrototypeOf(obj)方法是获取一个对象的原型,
const object1 = {}
const handler = {
getPrototypeOf:function(target){ //参数target是代理的对象
  console.log('getProto');
  return {} // 必须返回一个对象或null
  }
};
const p = new Proxy(object1, handler);
Object.getPrototypeOf(p) // 这里会打印出: 'getProto'
  1. handler.setPrototypeOf()是Object.setPrototypeOf方法的捕捉器。可以监听到Object.setPrototypeOf()和Reflect.setPrototypeOf()
var handlerReturnsFalse = {
    setPrototypeOf(target, newProto) {//参数target是代理的对象,newProto对象新原型或为null.
        return false; // 这里显示返回false则所有对于圆形的设置都会失败,及原型不会修改。但是需要注意到,在object调用时会返回一个类型错误,Reflect则会返回false。
    }
};

var newProto = {}, target = {};

var p1 = new Proxy(target, handlerReturnsFalse);
Object.setPrototypeOf(p1, newProto); // throws a TypeError
Reflect.setPrototypeOf(p1, newProto); // returns false
  1. handler.isExtensible()是Object.isExtensible()方法的捕捉器。isExtensible()方法是判定对象是否可以扩展。(是否可以在它上面添加新的属性)。
var p = new Proxy({}, {
  isExtensible: function(target) { //参数target是代理的对象
    console.log('called');
    return true; // 也可以 return 1; 等表示为 true 的值,这里必须返回一个布尔类型,
    // 或者可以转换为布尔类型的值,返回true是就是可以,返回false时会在object报错。
  }
});

console.log(Object.isExtensible(p)); // "called"
                                     // true
  1. handler.preventExtensions()是Object.preventExtensions方法的捕捉器。preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。

  2. handler.getOwnPropertyDescriptor()是Object.getOwnPropertyDescriptor方法的捕捉器.Object.getOwnPropertyDescriptor()方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性) Object.getOwnPropertyDescriptor(obj, prop)

    const object1 = {
      property1: 42
    };

    const descriptor1 = Object.getOwnPropertyDescriptor(object1, 'property1');
    console.log(descriptor1);// {configurable: true,enumerable: true,value: 42,writable: true}
  1. handler.defineProperty()是Object.defineProperty 方法的捕捉器。 Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
var p = new Proxy({}, {
  defineProperty: function(target, prop, descriptor) {
    console.log('called: ' + prop);
    return true;
  }
});

var desc = { configurable: true, enumerable: true, value: 10 };
Object.defineProperty(p, 'a', desc); // "called: a"
  1. handler.has()是in操作符的捕捉器。
  2. handler.get()属性读取操作的捕捉器。
var p = new Proxy({}, {
  get: function(target, prop, receiver) { // receiver 是Proxy 或者继承 Proxy 的对象
    console.log("called: " + prop);
    return 10; // 这里可以返回任意值,需要注意如果该对象的属性不可修改时,必须返回与对象属性相同的值,不然会报错。
  }
});

console.log(p.a); // "called: a"
                  // 10
  1. handler.set()方法是设置属性值操作的捕获器。
var p = new Proxy({}, {
  set: function(target, prop, value, receiver) { // target目标对象。
  // property将被设置的属性名或 Symbol
  // value新属性值。
  // receiver最初被调用的对象。通常是 proxy 本身,但 handler 的 set 方法也有可能在原型链上,或以其他方式被间接地调用(因此不一定是 proxy 本身)。
    target[prop] = value;
    console.log('property set: ' + prop + ' = ' + value);
    return true; // set()方法应当返回一个布尔值。true表示成功设置。
  }
})

p.a = 10;               // "property set: a = 10"
console.log(p.a);       // 10
  1. handler.deleteProperty()方法用于拦截对对象属性的 delete 操作。
var p = new Proxy({}, {
  deleteProperty: function(target, prop) {  // target目标对象。prop要删除的属性。
    console.log('called: ' + prop);
    return true; // 方法应当返回一个布尔值。true表示删除设置。
  }
});

delete p.a;
  1. handler.ownKeys()方法用于拦截 Reflect.ownKeys(),Object.getOwnPropertyNames(), ,Object.getOwnPropertySymbols() ,Object.keys()
var p = new Proxy({}, {
  ownKeys: function(target) {
    console.log('getkeys');
    return ['a', 'b', 'c'];
  }
});

console.log(Object.getOwnPropertyNames(p)) // getkeys // ['a', 'b', 'c'];
  1. handler.apply()函数调用操作的捕捉器。会拦截的操作proxy(...args), Function.prototype.apply() 和 Function.prototype.call(),Reflect.apply()
var p = new Proxy(function() {}, {
  apply: function(target, thisArg, argumentsList) { // target,目标对象(函数)。
  // thisArg, 被调用时的上下文对象。
  // argumentsList被调用时的参数数组。
    console.log('called: ' + argumentsList.join(', '));
    return argumentsList[0] + argumentsList[1] + argumentsList[2];
  }
});

console.log(p(1, 2, 3)); // "called: 1, 2, 3"
                         // 6
  1. ` handler.construct() 方法用于拦截 new 操作符。
var p = new Proxy(function() {}, {
  construct: function(target, argumentsList, newTarget) {
    // target,目标对象。
    // argumentsList 参数列表。
    // newTarget 最初被调用的构造函数。
    console.log('call: ' + argumentsList.join(', '));
    return { value: argumentsList[0] * 10 }; // 必须返回一个对象,否则会报错。
  }
});

console.log(new p(1).value); // "call: 1"
                             // 10