Proxy代理方法

Proxy

Proxy() 构造器用来创建 Proxy 对象。

其语法为

js 复制代码
const p = new Proxy(target, handler)

参数

  • target:为目标对象
  • handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为

handler

proxy.apply()

handler.apply() 方法用于拦截函数的调用

函数也是一个对象,Proxy一样可以代理函数,当函数对象被调用时,此方法被调用

如下代码

js 复制代码
function sum(a, b) {
  return a + b;
}

const handler = {
  apply: function (target, thisArg, argumentsList) {
    console.log(`sum: ${argumentsList}`);
    //    输出:"sum: 1,2"
    return target(argumentsList[0], argumentsList[1]) * 10;  //结果乘 10
  },
};


const proxy1 = new Proxy(sum, handler);

console.log(sum(1, 2)); //直接调用此函数时 输出:3

console.log(proxy1(1, 2));   //调用proxy代理的函数时 输出:30

参数

  • target :目标对象(函数)。在上面是sum函数
  • thisArg :被调用时的上下文对象。就是调用函数时的 this 值。
  • argumentsList:被调用时的参数数组。

返回值

对于返回值来说,aplly函数可以返回任何值

但是得返回函数操作,即是要调用目标函数,如果你想要目标函数执行的话

如下代码

js 复制代码
const targetFunction = function (a, b) {
  return a + b;
};

const proxy = new Proxy(targetFunction, {
  apply(target, thisArg, argumentsList) {
    console.log('函数调用');  
    console.log(`传递的参数:${argumentsList}`);   //输出:传递的参数:2,3
    return target.apply(thisArg, argumentsList)*10;
  }
});

console.log(proxy(2, 3));  //输出:50


const targetFunction = function (a, b) {
  return a + b;
};

const proxy = new Proxy(targetFunction, {
  apply(target, thisArg, argumentsList) {
    console.log('函数调用');  
    console.log(`传递的参数:${argumentsList}`);   //输出:传递的参数:2,3
    return 1;   //返回 1
  }
});

console.log(proxy(2, 3));  //不管函数参数怎么样 都是输出:1

拦截操作

  • proxy(...args)

    上面已经有的调用方式,就不多说了

  • Function.prototype.apply() 和 Function.prototype.call()

    有以下例子

    js 复制代码
    const targetFunction = function (a, b) {
      return a + b;
    };
    
    const proxy = new Proxy(targetFunction, {
      apply(target, thisArg, argumentsList) {
        console.log('拦截目标函数的调用');
        console.log(`传递的参数:${argumentsList}`);
        return target.apply(thisArg, argumentsList);
      }
    });
    
    proxy.apply(null, [2, 3]); //   输出:   拦截目标函数的调用  传递的参数:2,3
    //这个apply是改变函数this指向的方法,不是proxy上的
    
    
    const targetFunction = function (a, b) {
      return a + b;
    };
    
    const proxy = new Proxy(targetFunction, {
      apply(target, thisArg, argumentsList) {
        console.log('拦截目标函数的调用');
        console.log(`传递的参数:${argumentsList}`);
        return target.apply(thisArg, argumentsList);
      }
    });
    
    proxy.call(null, 2, 3); // 输出: 拦截目标函数的调用 传递的参数:2,3
    //call也是改变函数this指向并且执行的
  • Reflect.apply()

    js 复制代码
    Reflect.apply(targetFunction, thisArg, argumentsList)
    //这个也是改变this指向JavaScript 中的一个静态方法

proxy.construct()

handler.construct() 方法用于拦截 new 操作符。为了使 new 操作符在生成的 Proxy 对象上生效,用于初始化代理的目标对象自身必须具有 [[Construct]] 内部方法(即 new target 必须是有效的)。

就是对于普通对象不生效(得构造函数)

看如下例子代码

js 复制代码
function monster1(disposition) {
  this.disposition = disposition;
}

const handler1 = {
  construct(target, args) {
    console.log(`Creating a ${target.name}`);  //当new proxy1时触发
    // Creating a monster1
    return new target(...args);
  },
};

const proxy1 = new Proxy(monster1, handler1);

console.log(new proxy1('fierce').disposition);  //上面输出 Creating a monster1   
//这里输出 fierce

其语法

js 复制代码
var p = new Proxy(target, {
  construct: function (target, argumentsList, newTarget) {},
});

参数

  • target:目标对象
  • argmentsList:constructor 的参数列表
  • newTarget:最初被调用的构造函数,就上面的例子而言是 p

返回值

construct必须返回一个对象!!!否则将会抛出错误

拦截操作

下面是演示如何拦截new操作

js 复制代码
var p = new Proxy(function () {}, {
  construct: function (target, argumentsList, newTarget) {
    console.log("called: " + argumentsList.join(", "));
    return { value: argumentsList[0] * 10 };
  },
});

console.log(new p(1).value); // "called: 1"; 输出:10

handler.get()

handler.get() 方法用于拦截对象的读取属性操作。

说白了就是会拦截读取对象里面值的函数

其语法是

js 复制代码
var p = new Proxy(target, {
  get: function (target, property, receiver) {},
});

参数

  • **target:**目标对象
  • property:被获取的属性名
  • receiver: Proxy 或者继承 Proxy 的对象

返回值

get方法可以返回任何值

拦截操作

  • 访问属性:proxy[foo] 和 proxy.bar
  • 访问原型链上的属性:Object.create(proxy)[foo]
  • Reflect.get()

如果违背了以下的约束,proxy 会抛出 TypeError

  • 如果要访问的目标属性是不可写以及不可配置的,则返回的值必须与该目标属性的值相同。
  • 如果要访问的目标属性没有配置访问方法,即 get 方法是 undefined 的,则返回值必须为 undefined

如下就会抛出错误

js 复制代码
var obj = {};
Object.defineProperty(obj, "a", {
  configurable: false, //不可配置
  enumerable: false,  
  value: 10,
  writable: false, //不可写
});

var p = new Proxy(obj, {
  get: function (target, prop) {
    return 20;
  },
});

p.a; //会抛出 TypeError

handler.set()

handler.set() 方法是设置属性值操作的捕获器。

看如下代码

js 复制代码
const monster1 = { eyeCount: 4 };

const handler1 = {
  set(obj, prop, value) {
    if (prop === 'eyeCount' && value % 2 !== 0) {
      console.log('Monsters must have an even number of eyes');
    } else {
      return Reflect.set(...arguments);
    }
  },
};

const proxy1 = new Proxy(monster1, handler1);

proxy1.eyeCount = 1;
//输出 "Monsters must have an even number of eyes"

console.log(proxy1.eyeCount);
// 输出 4

proxy1.eyeCount = 2;
console.log(proxy1.eyeCount);
// 输出 2

参数

  • target:目标对象
  • property:将被设置的属性名或 Symbol
  • value:新属性值
  • receiver:最初被调用的对象。通常是 proxy 本身,但 handler 的 set 方法也有可能在原型链上,或以其他方式被间接地调用(因此不一定是 proxy 本身

返回值

set() 方法应当返回一个布尔值。

  • 返回 true 代表属性设置成功。(返回与否,只要修改了照样可以)
  • 在严格模式下,如果 set() 方法返回 false,那么会抛出一个 TypeError 异常。

拦截操作

  • 指定属性值:proxy[foo] = barproxy.foo = bar
  • 指定继承者的属性值:Object.create(proxy)[foo] = bar
  • Reflect.set()

在以下情况下程序报错

  • 若目标属性是一个不可写及不可配置的数据属性,则不能改变它的值。
  • 如果目标属性没有配置存储方法,即 [[Set]] 属性的是 undefined,则不能设置它的值。
  • 在严格模式下,如果 set() 方法返回 false,那么也会抛出一个 TypeError 异常。

handler.has()

handler.has() 方法是针对 in 操作符的代理方法。

如下代码示例

js 复制代码
const handler1 = {
  has(target, key) {
    if (key[0] === '_') {
      return false;
    }
    return key in target;
  },
};

const monster1 = {
  _secret: 'easily scared',
  eyeCount: 4,
};

const proxy1 = new Proxy(monster1, handler1);
console.log('eyeCount' in proxy1);
// 输出 true

console.log('_secret' in proxy1);
// 输出 false   //

console.log('_secret' in monster1);
// 输出 true

当我们使用 in 操作符的时候就会触发此操作

语法如下

js 复制代码
var p = new Proxy(target, {
  has: function (target, prop) {},
});

参数

  • target:目标对象
  • 目标对象

返回值

has 方法返回一个 boolean 属性的值。

拦截操作

  • 属性查询:foo in proxy
  • 继承属性查询:foo in Object.create(proxy)
  • with 检查: with(proxy) { (foo); }
  • Reflect.has()

该方法不可用时是

  • 在 JavaScript 中,当目标对象的某个属性本身被设置为不可配置 (configurable: false) 时,这意味着这个属性的配置不可被改变,无法通过代理隐藏这个属性。

    看如下代码

    js 复制代码
    // 创建一个对象并定义一个属性,将其设置为不可配置
    var obj = {
      name: 'John'
    };
    
    Object.defineProperty(obj, 'name', {
      configurable: false // 设置为不可配置
    });
    
    // 使用代理拦截 'has' 方法
    var proxyObj = new Proxy(obj, {
      has: function(target, prop) {
        if (prop === 'name') {
          return false; // 在这里希望代理隐藏属性
        } else {
          return prop in target;
        }
      }
    });
    
    console.log('name' in proxyObj); // 输出还是"true",这就是无法通过代理隐藏不可配置的属性
  • 当目标对象被设置为不可扩展 (Object.preventExtensions(obj)) 时,意味着无法向该对象添加新属性。这种情况下,即使通过代理也无法隐藏该对象原有的属性。

    看如下代码

    js 复制代码
    // 创建一个不可扩展的对象
    var obj = {
      name: 'John'
    };
    Object.preventExtensions(obj);
    
    // 尝试通过代理隐藏属性
    var proxyObj = new Proxy(obj, {
      has: function(target, prop) {
        return false; // 代理隐藏属性,但是无法隐藏原始对象的属性
      }
    });
    
    console.log('name' in proxyObj); // 输出 true,代理无法隐藏不可扩展对象的属性

    这个对象里的属性不可被代理隐藏

handler.ownKeys()

handler.ownKeys() 方法用于拦截 Reflect.ownKeys()

对于这个属性我们需要了解以下什么才是Reflect.ownKeys()

静态方法 Reflect.ownKeys() 返回一个由目标对象自身的属性键组成的数组

换句话说他就是可以返回目标对象的自身属性组成的数组

注意了是自身的!!!

我们看一看它的示例代码

js 复制代码
Reflect.ownKeys({ z: 3, y: 2, x: 1 }); // [ "z", "y", "x" ]
Reflect.ownKeys([]); // ["length"]   //空数组自带length属性

var sym = Symbol.for("comet");
var sym2 = Symbol.for("meteor");
var obj = {
  [sym]: 0,
  str: 0,
  773: 0,
  0: 0,
  [sym2]: 0,
  "-1": 0,
  8: 0,
  "second str": 0,
};
Reflect.ownKeys(obj);
// [ "0", "8", "773", "str", "-1", "second str", Symbol(comet), Symbol(meteor) ]
// Indexes in numeric order,  数值类型排前面
// strings in insertion order,   字符串中间
// symbols in insertion order    Symbol后面

我们回到主题 **handler.ownKeys()**拦截 Reflect.ownKeys()

那它是怎么样拦截的。我们看一看如下代码

js 复制代码
const monster1 = {
  _age: 111,
  [Symbol('secret')]: 'I am scared!',
  eyeCount: 4,
};

const handler1 = {
  ownKeys(target) {
    return Reflect.ownKeys(target);
  },
};

const proxy1 = new Proxy(monster1, handler1);

for (const key of Object.keys(proxy1)) {   //也可以拦截 Object.keys
  console.log(key);
  //输出 "_age"
  // 输出 "eyeCount"
}

参数

只有 target:目标对象

返回值

ownKeys方法必须返回一个数组

拦截操作

但是ownKeys只能符合以下情况下不会报错

  • ownKeys 的结果必须是一个数组。
  • 数组的元素类型要么是一个 String ,要么是一个 Symbol.
  • 结果列表必须包含目标对象的所有不可配置(non-configurable)、自有(own)属性的 key.
  • 如果目标对象不可扩展,那么结果列表必须包含目标对象的所有自有(own)属性的 key,不能有其他值。
相关推荐
Мартин.8 分钟前
[Meachines] [Easy] Sea WonderCMS-XSS-RCE+System Monitor 命令注入
前端·xss
昨天;明天。今天。2 小时前
案例-表白墙简单实现
前端·javascript·css
数云界2 小时前
如何在 DAX 中计算多个周期的移动平均线
java·服务器·前端
风清扬_jd2 小时前
Chromium 如何定义一个chrome.settingsPrivate接口给前端调用c++
前端·c++·chrome
安冬的码畜日常2 小时前
【玩转 JS 函数式编程_006】2.2 小试牛刀:用函数式编程(FP)实现事件只触发一次
开发语言·前端·javascript·函数式编程·tdd·fp·jasmine
ChinaDragonDreamer2 小时前
Vite:为什么选 Vite
前端
小御姐@stella2 小时前
Vue 之组件插槽Slot用法(组件间通信一种方式)
前端·javascript·vue.js
GISer_Jing2 小时前
【React】增量传输与渲染
前端·javascript·面试
GISer_Jing2 小时前
WebGL在低配置电脑的应用
javascript
eHackyd2 小时前
前端知识汇总(持续更新)
前端