【吃透】JS代理(Proxy)

jym好😘,我是月弦笙音,今天给大家分享如何从根本上吃透js代理,嘎嘎的😍,看下面

咱们首先得知道:

代理是 js 中的对象,它允许你创建对象的代理,同时还可以为标准对象操作定义自定义行为。这意味着,譬如,如果有人试图从对象中获取属性的值,你可以定义一组自定义行为。这使代理变成了一个非常强大的工具,所以让我们看看它们是如何工作的。get set has

一、js 代理的基础知识✔

en... 上面听起来很复杂,所以让我们看一个没有任何方法的简单例子,首先,可以使用构造函数创建代理,该构造函数new Proxy()接受两个参数:

  • 这是原始对象。target
  • 这是我们将添加到对象之上的方法或属性集。handler

可以包含预定义方法的列表。例如,如果我们为 定义一个方法,它将自定义当我们尝试从对象中获取项目时会发生神马?handler get

typescript 复制代码
let target = {
    firstName: "月",
    lastName: "弦",
    age: 152
}

let handler = {
    get: (object, prop) => {
        console.log(`Hi ${object.firstName} ${object.lastName}`)
    }
}

let proxyExample = new Proxy(target, handler);

proxyExample.age; // "Hi 月 弦"

由于我们尝试在代理上找到的值,因此触发了自定义处理程序 - 因此我们控制台记录了正如你所看到的,这可以成为一个非常强大的工具,因为当调用一个对象的标准操作时,你可以做各种各样的事情。🥰get proxyExample.age get Hi ${object.firstName} ${object.lastName}

请注意,当我们添加到上面时,我们有一些自定义参数。可以添加到代理的每个处理程序都带有一组自定义参数。get handler

因为使用的功能是:get(object, prop, receiver)

  • object- 原始对象。在上面的示例中,这是包含 和 的对象。firstName``lastName``age
  • prop- 试图更改的属性。在上面的示例中,.get``age
  • reciever- 代理本身。

1. 处理程序方法称为"陷阱"💭

代理中的处理程序方法通常称为陷阱,因此,如果你看到这个术语四处乱窜,请不要感到困惑。之所以这样称呼它们,是因为它们"捕获"对象操作并执行某种自定义代码。例如,每当有人尝试获取代理的属性时,控制台都会记录一些内容。🧐

2. 更新代理值

代理仍引用原始对象,因此对象值和代理值的引用相同。因此,如果你尝试更新代理的值,它也会更新原始对象的值。例如,下面我尝试更新代理,如你所见,原始对象和代理都已更新:

ini 复制代码
let target = {
    name: "月",
    age: 152
}

let handler = {
}

let proxyExample = new Proxy(target, handler);
proxyExample.name = "Dave";

console.log(proxyExample.name); // Dave
console.log(target.name); // Dave

了解这一点很有用 - 不要指望代理会完全创建一个单独的对象 - 这不是复制对象的方法。

二、 js代理中的自定义处理程序

代理有许多自定义处理程序,允许我们基本上"捕获"任何对象操作并用它做一些有趣的事情。最常用的方法是👍:

  • proxy.apply(objects, thisObject, argList)- 捕获函数调用的方法。
  • proxy.construct(object, argList, newTarget)- 使用 constructor 关键字调用函数时捕获的方法。new
  • proxy.defineProperty(object, prop, descriptor)- 使用将新属性添加到对象时捕获的方法。Object.defineProperty
  • proxy.deleteProperty(object, prop)- 从对象中删除属性时捕获的方法。
  • proxy.get(object, prop, receiver)- 如前所述,当有人试图从对象获取属性时,一种陷阱的方法。get
  • proxy.set(object, prop, value, receiver)- 当属性被赋予值时捕获的方法。
  • proxy.has(object, prop)- 一种诱捕操作员的方法。in

上面的方法足以完成你想用代理做的几乎所有事情。它们很好地覆盖了所有主要对象操作,可以根据需要进行修改和自定义。

不过,还有一些 - 因此,除了这些非常基本的对象操作外,我们还可以访问:

  • proxy.getPrototypeOf(object)- 捕获方法的方法。Object.getPrototypeOf
  • proxy.getOwnPropertyDescriptor(object, prop)- 捕获 的方法,它返回特定属性的描述符 - 例如,它是可枚举的,等等。getOwnPropertyDescriptor
  • proxy.isExtensible(object)- 一种在触发时捕获的方法。Object.isExtensible()
  • proxy.preventExtensions(object)- 一种在触发时捕获的方法。Object.preventExtensions()
  • proxy.setPrototypeOf(object, prototype)- 一种在触发时捕获的方法。Object.setPrototypeOf()
  • proxy.ownKeys(object)- 一种在触发 like 方法时捕获的方法。Object.getOwnPropertyNames()

让我们更详细地看一下其中的一些,以了解代理的工作原理。

三、在代理中使用 in 运算符

我们已经介绍了,那么让我们看看。这主要在我们使用运算符时触发。例如,如果我们想通过控制台记录使用时属性不存在的事实,我们可以执行如下操作:proxy.get() has() in in

typescript 复制代码
let target = {
    firstName: "月",
    lastName: "弦",
    age: 152
}

let handler = {
    has: (object, prop) => {
        if(object[prop] === undefined) {
            console.log('Property not found');
        }
        return object[prop]
    }
}

let proxyExample = new Proxy(target, handler);

console.log('address' in proxyExample); 
// 'Property not found' 
// false

由于address未在target中未定义,将返回 false,正如我们在代理中定义的那样。address target proxyExample address' in proxyExample Property not found

四、 使用代理设置值

你可能想要修改的类似有用方法,下面,我使用自定义处理程序来修改当我们尝试更改用户年龄时发生的情况。对于每个设置操作,如果属性是一个数字,那么当数字更新时,我们将记录差异。set()🧐

typescript 复制代码
let target = {
    firstName: "月",
    lastName: "弦",
    age: 152
}

let handler = {
    set: (object, prop, value) => {
        if(typeof object[prop] === "number" && typeof value === "number") {
            console.log(`Change in number was ${value - object[prop]}`);
        }
        return object[prop]
    }
}

let proxyExample = new Proxy(target, handler);

proxyExample['age'] = 204;
// Change in number was 52

由于 和 更新后的值都是数字,因此我们不仅将值更新 ,而且还会得到一个有用的控制台日志,告诉我们两个数字之间的区别是什么。很酷,对吧?proxyExample.age 204

虽然会针对任何设置的操作(包括向对象添加新项)触发,但你也可以使用 实现类似的行为。例如,这也将起作用:set defineProperty

typescript 复制代码
let target = {
    firstName: "月",
    lastName: "弦",
    age: 152
}

let handler = {
    defineProperty: (object, prop, descriptor) => {
        console.log(`A property was set - ${prop}`);
    },
}

let proxyExample = new Proxy(target, handler);

proxyExample['age'] = "123 Fake Street";
// A property was set - address

但是请注意,如果你将 和 两者都添加为处理程序,则在我们使用方括号或表示法设置属性的情况下将覆盖。 但是,如果你显式使用,仍会触发,如下所示:set defineProperty set defineProperty[] defineProperty Object.defineProperty

typescript 复制代码
let target = {
    firstName: "月",
    lastName: "弦",
    age: 152
}

let handler = {
    defineProperty: (object, prop, descriptor) => {
        console.log(`A property was set with defineProperty - ${prop}`);
        return true;
    },
    set: (object, prop, descriptor) => {
        console.log(`A property was set - ${prop}`);
        return true;
    },
}

let proxyExample = new Proxy(target, handler);

Object.defineProperty(proxyExample, 'socialMedia', {
    value: 'twitter',
    writable: false
});
proxyExample['age'] = "123 Fake Street";
// A property was set with defineProperty - socialMedia
// A property was set - address

五、使用代理删除值

除了这些有用的方法外,我们还可以使用这些方法来处理如果用户使用关键字删除某些内容时会发生什么。例如,我们可以让某人知道属性正在被删除:deleteProperty delete

typescript 复制代码
let target = {
    firstName: "月",
    lastName: "弦",
    age: 152
}

let handler = {
    deleteProperty: (object, prop) => {
        console.log(`Poof! The ${prop} property was deleted`);
    },
}

let proxyExample = new Proxy(target, handler);

delete proxyExample['age'];
// Poof! The age property was deleted

六、使用代理自定义函数调用❤

代理还允许我们在想要调用函数时运行自定义代码。这是因为函数是对象的 Javascript 怪癖。有两种方法可以做到这一点:

  • 替换为 handler,用于捕获标准函数调用。apply()
  • 与处理程序一起使用,该处理程序捕获构造函数调用。construct() new

下面是一个快速示例,我们捕获一个函数调用,并通过在其输出末尾附加一些内容来修改它

typescript 复制代码
let target = (firstName, lastName) => {
    return `Hello ${firstName} ${lastName}`
}

let handler = {
    apply: (object, thisObject, argsList) => {
        let functionCall = object(...argsList);
        return `${functionCall}. I hope you are having a nice day!`
    },
}

let proxyExample = new Proxy(target, handler);

proxyExample("月", "弦");
// Returns
// Hello 月 弦. I hope you are having a nice day!

apply接受三个参数:

  • object- 原始对象。
  • thisObject- 函数/对象的值。this
  • argsList- 传递给函数的参数。

上面,我们使用包含原始函数的参数调用了我们的函数。然后我们在它的末尾添加了一些文本来更改函数的输出。再说一次,很酷,对吧?object target

我们也可以使用来做同样的事情,它也有三个参数:construct

  • object- 原始对象。
  • argsList- 函数/对象的参数。
  • newTarget- 最初调用的构造函数 - 即代理。

下面是一个函数返回一个对象的示例,我们使用代理上的方法向其添加更多属性:construct

javascript 复制代码
function target(a, b, c) {
    return { 
        a: a,
        b: b,
        c: c
    }
}

let handler = {
    construct: (object, argsList, newTarget) => {
        let functionCall = object(...argsList);
        return { ...functionCall, d: 105, e: 45 }
    },
}

let proxyExample = new Proxy(target, handler);

new proxyExample(15, 24, 45);
// Returns
// {a: 15, b: 24, c: 45, d: 105, e: 45}

七、最后

代理是 js 武器库中一个了不起的工具,可让你修改对象的基本操作。这里有大量的方法可以玩,如果你正确使用它们,它们可以大大简化你的代码。最后我希望jym喜欢这篇文章😂

感谢jym浏览本文,共同进步🤞,若有更好的建议,欢迎评论区讨论哈🌹。

相关推荐
前端李易安28 分钟前
Web常见的攻击方式及防御方法
前端
PythonFun1 小时前
Python技巧:如何避免数据输入类型错误
前端·python
Neituijunsir1 小时前
2024.09.22 校招 实习 内推 面经
大数据·人工智能·算法·面试·自动驾驶·汽车·求职招聘
hakesashou1 小时前
python交互式命令时如何清除
java·前端·python
天涯学馆1 小时前
Next.js与NextAuth:身份验证实践
前端·javascript·next.js
HEX9CF1 小时前
【CTF Web】Pikachu xss之href输出 Writeup(GET请求+反射型XSS+javascript:伪协议绕过)
开发语言·前端·javascript·安全·网络安全·ecmascript·xss
ConardLi1 小时前
Chrome:新的滚动捕捉事件助你实现更丝滑的动画效果!
前端·javascript·浏览器
ConardLi1 小时前
安全赋值运算符,新的 JavaScript 提案让你告别 trycatch !
前端·javascript
凌云行者2 小时前
使用rust写一个Web服务器——单线程版本
服务器·前端·rust
华农第一蒟蒻2 小时前
Java中JWT(JSON Web Token)的运用
java·前端·spring boot·json·token