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,不能有其他值。
相关推荐
WeiXiao_Hyy6 分钟前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
吃杠碰小鸡23 分钟前
高中数学-数列-导数证明
前端·数学·算法
kingwebo'sZone29 分钟前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_09011 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农1 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king1 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳2 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵2 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星2 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_3 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js