몇 달 전부터 이 글을 써야겠다는 생각이 있었는데, 이 지식 포인트를 생각해보니 VueJs
가 많이 쓰이는데, 저는 React
)입니다. > 개발자. 다른 프레임워크를 선택하라는 요청을 받으면 angular
를 선호할 수도 있지만 최근 NestJs
를 배우고 있는데 Reflect
가 다음과 같이 하단에 널리 사용됩니다. VueJs
用得比较多,我本身是一个 React
开发者,如果让我选另外一个框架的话我可能更倾向 angular
,但是最近在学 NestJs
,发现底层大量使用的 Reflect
,具体如下:
好了,到了这里我就是我今天为什么会写这篇文章的原因了,本篇文章将会按照 Object
-> Proxy
-> Reflect
的顺序进行讲解,赶紧学起来吧!
在 ES5
中,我们可以定义一个对象,并且对其进行操作(添加或查找),如下代码所示:
const moment = { age: 18, address: "西安", }; moment["hobby"] = "basketball"; moment.hight = 1.59; console.log(moment.age); // 18
那如果我们要对该对象进行监听呢,我们希望可以监听到这个对象中的属性被设置或获取的过程,我们可以通过属性描述符中的存储属性来做到这个功能。
在 ES5
中,所有的属性都具备了属性描述符,具体使用如下图所示:
就像上图所展示的一样,这个普通的对象属性对应的属性描述符,可不仅仅是一个18,它还包含另外三个特性,它们分别是:
writable
;enumerable
;configurable
;在创建普通户型时,属性描述符会使用默认值,我们可以使用 Object.defineProperty(...)
来添加一个新属性或者修改一个已有属性(当该属性 Configurable
的值为 true
时)并对特性进行设置,具体如下代码所示:
const moment = { age: 18, address: "西安", }; Object.defineProperty(moment, "address", { value: "肇庆", writable: true, configurable: true, enumerable: true, }); console.log(moment.address); // 肇庆
该方法接收三个参数,它们分别为:
Symbol
;前面说了这么多基础的东西,但是还没有讲解是怎么接收到属性的变化的,在这里,属性描述符 Object.defineProperty
提供了两个属性,它们分别是 set
和 get
,两个属性的使用如下所示:
const moment = { age: 18, address: "西安", }; Object.keys(moment).forEach((key) => { let value = moment[key]; Object.defineProperty(moment, key, { get: function () { console.log(`监听到了对象的 ${key} 属性被访问了`); return value; }, set: function (params) { console.log(`监听到了对象的 ${key} 属性被修改了`); value = params; }, }); }); moment.age = 22; const foo = moment.address;
当我们对 moment.age
进行赋值的时候,会调用 set
属性上的方法,最终会输出 监听到了对象的 age 属性被修改了
。
当我们对 moment.address
进行取值的时候,会调用 get
属性上的方法,最终会输出 监听到了对象的 address 属性被访问了
。
虽然这两个方法是能做到,但是如果我们想监听更加丰富的操作的时候是做不到的,例如新增属性,淡出属性,使用 Object.defineProperty(...)
是做不到的
最终结果如上图所示,终端中的输出是对应上面画圈圈的代码执行。
那么 Proxy
的出现就很好的解决了这一痛点。
在 ES6
中,新增了一个 Proxy
类,这个类从名字就可以看出来,是用于帮助我们创建一个代理的,它是一种由你创建的特殊对象,它封装另一个普通对象或者说挡在这个普通对象的前面,例如你要修改一个对象,它主要有以下的流程图:
它就像一个关卡对你的操作进行拦截。
这个类的基本使用如下所示:
const moment = { age: 18, address: "西安", }; const proxy = new Proxy(moment, {}); console.log(proxy); // { age: 18, address: '西安' }
你会发现它返回的是同一个对象,但是内存地址不同,所以通过严格相等比较返回的值也就是 false
。
Peoxy
总共有13个捕获器,但是常用的就那么几个,这里不低其全部讲解,具体可以查看 MDN官网
话不多说,直接上代码:
const moment = { age: 18, address: "西安", }; function foo(x, y) { return x + y; } const proxy = new Proxy(moment, { has: function (target, prop) { console.log(`使用 in 访问了 ${prop} 是否存在于 moment 对象`); }, get: function (target, property, receiver) { console.log(`通过读取 moment 对象中的 ${property} 属性`); }, set: function (target, property, value, receiver) { console.log(`通过设置 moment 对象中的 ${property} 属性为 ${value}`); }, }); const fProxy = new Proxy(foo, { apply: function (target, _, params) { return target(...params) * 10; }, construct: function (target, argumentsList, newTarget) { console.log(target); // [Function: foo] console.log(argumentsList); // [ 1, 2 ] console.log(newTarget); // [Function: foo] return {}; }, }); "age" in proxy; // 使用 in 访问了 age 是否存在于 moment 对象 proxy.age; // 通过读取 moment 对象中的 age 属性 proxy.address = "肇庆"; // 通过设置 moment 对象中的 address 属性为 肇庆 console.log(foo(1, 2)); // 3 console.log(fProxy(1, 2)); // 30 new fProxy(1, 2);
在上面的代码中,target === moment
和 receiver === proxy
都返回 true
,也就是说 target
为对应要修改的对象,而 receiver
为 Proxy
或者继承 Proxy
Object
-> ; Reflect
가 순서대로 설명되어 있으니 빠르게 배워보세요 🎜ES5
에서는 다음 코드에 표시된 대로 객체를 정의하고 해당 객체에 대해 작업(추가 또는 찾기)을 수행합니다. 🎜const moment = { age: 18, address: "西安", }; const { proxy, revoke } = Proxy.revocable(moment, { get: function (target, key, receiver) { console.log("get 捕获器"); }, }); proxy.address; revoke(); proxy.address;🎜 따라서 이 객체를 모니터링하려면 이 객체의 속성을 설정하거나 가져오는 프로세스를 모니터링해야 합니다. 속성 설명자에 저장된 속성을 전달하여 이를 수행할 수 있습니다. 🎜
ES5
에서는 모든 속성에 속성 설명자가 있습니다. 구체적인 사용법은 아래 그림과 같습니다. 🎜 🎜🎜🎜좋아요 위 그림에서 이 일반 개체 속성에 해당하는 속성 설명자는 단지 18이 아니라 다음과 같은 세 가지 다른 특성도 포함합니다. 🎜쓰기 가능
;열거 가능
;구성 가능
;Object.defineProperty(...)
를 사용하여 새 속성을 추가하거나 기존 속성을 수정할 수 있습니다(속성이 값이 Configurable일 때 code>가 <code>true
임) 다음 코드에 표시된 대로 특성을 설정합니다. 🎜const target = { moment() { return this === proxy; }, }; const proxy = new Proxy(target, {}); console.log(target.moment()); // false console.log(proxy.moment()); // true🎜이 메서드는 다음과 같은 세 가지 매개 변수를 받습니다. 🎜
기호
;Object.defineProperty
는 set
및 get
이라는 두 가지 속성을 제공합니다. 두 속성의 사용은 다음과 같습니다. 🎜const wm = new WeakMap(); class User { constructor(userId) { wm.set(this, userId); } set id(userId) { wm.set(this, userId); } get id() { return wm.get(this); } }🎜
moment.age
에 값을 할당하면 set
속성의 메서드가 호출되고 결국 가 출력되고 객체의 age 속성이
가 수정되었습니다. 🎜🎜 moment.address
값을 얻으면 get
속성에 대한 메서드가 호출되고 마지막으로 출력은 주소 속성을 모니터링합니다.
개체에 액세스합니다. 🎜🎜이 두 가지 방법을 사용할 수 있지만 더 풍부한 작업을 모니터링하려면 속성을 추가하고 속성을 페이드 아웃하는 등의 작업을 수행할 수 없습니다. Object.defineProperty(...)
를 사용하면 됩니다. 이러지 마세요🎜🎜🎜🎜최종 결과는 위 그림과 같이 터미널에 출력되는 것은 위의 원 안의 코드를 실행한 것과 같습니다. 🎜🎜그러면 Proxy
의 등장으로 이러한 문제점이 아주 잘 해결되었습니다. 🎜ES6
에는 새로운 Proxy
클래스가 추가되었습니다. 이 클래스는 다음으로 식별할 수 있습니다. 이름은 프록시를 만드는 데 사용된다는 것을 알 수 있습니다. 이는 사용자가 만든 특수 개체입니다. 예를 들어 개체를 수정하려는 경우 이 일반 개체 앞에 다른 일반 개체나 블록이 포함되어 있습니다. , 주로 다음 흐름도를 가지고 있습니다: 🎜🎜🎜🎜당신의 작전을 가로채는 레벨과 같습니다. 🎜🎜이 클래스의 기본 사용법은 다음과 같습니다. 🎜const user = new User(1); console.log(user.id); // 1 const proxy = new Proxy(user, {}); console.log(proxy.id); // undefined🎜동일한 객체를 반환하지만 메모리 주소가 다르기 때문에 엄격한 동등 비교를 통해 반환된 값은
false
입니다. 🎜페옥시
총 13개의 캡처러가 있지만 일반적으로 많이 사용되는 것은 소수에 불과해 낮지 않습니다. 여기에서 모든 설명을 보려면 MDN 공식 홈페이지🎜🎜말할 것도 없이 바로 코드로 넘어가겠습니다:🎜const UserProcess = new Proxy(User, {}); const proxy = new UserProcess(1); console.log(proxy.id); // 1🎜위 코드에서
target == = moment
및 receiver === Proxy
는 모두 true
를 반환합니다. 이는 target
이 대상 개체에 해당함을 의미합니다. 수정될 수 있으며 receiver
는 Proxy
또는 Proxy
를 상속하는 개체입니다. 🎜操作 Proxy
的同时会修改 moment
对象。
普通对象总是陷入到目标对象,并且在创建之后不能改变,只要还保持着对这个代理的引用,代理的机制就将维持下去。
但是可能会存在这样的情况,比如你想要创建一个在你想要停止它作为代理时便可被停用的代理,解决的方案是创建可取消代理,具体代码如下所示:
const moment = { age: 18, address: "西安", }; const { proxy, revoke } = Proxy.revocable(moment, { get: function (target, key, receiver) { console.log("get 捕获器"); }, }); proxy.address; revoke(); proxy.address;
最终的输出如下图所示:
一旦可取消代理被取消,任何对他的访问都会抛出 TypeError
错误。
尽管现在 Proxy
已经做得很好了,但是在某些情况下,代理也不能与现在的 ECMAScript
机制很好地协同。
Peoxy
潜在的一个问题来源是 this
值,我们知道方法中的 this
通常执行调用这个方法的对象,具体代码如下所示:
const target = { moment() { return this === proxy; }, }; const proxy = new Proxy(target, {}); console.log(target.moment()); // false console.log(proxy.moment()); // true
按照正常的理解这是没有问题的调用 Proxy
上的任何方法,而这个方法进而又会调用另一个方法,实际上都会调用 Proxy
内部的方法,这是符合预期的行为,但是,如果目标对象依赖于对象表示,那就可能碰到意料之外的问题。
举个例子:
const wm = new WeakMap(); class User { constructor(userId) { wm.set(this, userId); } set id(userId) { wm.set(this, userId); } get id() { return wm.get(this); } }
由于这个实现依赖 User
实例的对象标识,在这个实例被代理的情况下就会出现问题:
const user = new User(1); console.log(user.id); // 1 const proxy = new Proxy(user, {}); console.log(proxy.id); // undefined
这是因为 User
实例一开始使用目标对象作为 WeakMap
的键,代理对象却尝试从自身取得这个实例,要解决这个问题,就需要重新设置代理,把代理 User
实例改为代理 User
类本身,之后再创建代理的实例就会创建代理实例作为 WeakMap
的键了:
const UserProcess = new Proxy(User, {}); const proxy = new UserProcess(1); console.log(proxy.id); // 1
代理与内置引用类型的实例通常可以很好地协同,但有些 ECMAScript
内置类型可能会依赖代理无法控制的机制,结果导致在代理上调用某些方法会出错,具体代码如下所示:
const target = new Date(); const proxy = new Proxy(target, {}); console.log(proxy instanceof Date); // true proxy.getDate(); // TypeError: this is not a Date object.
在上面的代码中,根据 ECMAScript
规范,Date
类型方法的执行依赖 this
值上的内部曹伟 [[NumberDate]]
,代理对象上不存在这个内部槽位,而且这个内部槽位的值也不能通过普通的 get()
和 set()
操作访问到,于是代理拦截后本应该转发给目标对象的方法会抛出 TypeRrror
的错误。
这篇文章到此结束,下一篇将会详细讲解 Reflect
,敬请期待。
위 내용은 Vue를 몰라도 괜찮지만, Proxy 사용법은 꼭 알아야 합니다!의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!