이번에는 Proxy 사용 사례에 대한 자세한 설명을 가져왔습니다. Proxy 사용 시 주의 사항은 무엇인가요? 실제 사례를 살펴보겠습니다.
우선 프록시
가 무엇을 의미하는지 이해해야 합니다. 번역하면 이 단어는 프록시입니다. Proxy
是什么意思,这个单词翻译过来,就是 代理。
可以理解为,有一个很火的明星,开通了一个微博账号,这个账号非常活跃,回复粉丝、到处点赞之类的,但可能并不是真的由本人在维护的。
而是在背后有一个其他人 or 团队来运营,我们就可以称他们为代理人,因为他们发表的微博就代表了明星本人的意思。
P.S. 强行举例子,因为本人不追星,只是猜测可能会有这样的运营团队
这个代入到<a href="http://www.php.cn/wiki/48.html" target="_blank">JavaScript</a>
当中来,就可以理解为对对象
或者函数
的代理操作。
Proxy是ES6中提供的新的API,可以用来定义对象各种基本操作的自定义行为
(在文档中被称为traps
,我觉得可以理解为一个针对对象各种行为的钩子)
拿它可以做很多有意思的事情,在我们需要对一些对象的行为进行控制时将变得非常有效。
创建一个Proxy
的实例需要传入两个参数
target
要被代理的对象,可以是一个object
或者function
handlers
对该代理对象的各种操作行为处理
let target = {} let handlers = {} // do nothing let proxy = new Proxy(target, handlers) proxy.a = 123 console.log(target.a) // 123
在第二个参数为空对象的情况下,基本可以理解为是对第一个参数做的一次浅拷贝
(Proxy必须是浅拷贝,如果是深拷贝则会失去了代理的意义)
就像上边的示例代码一样,如果没有定义对应的trap
,则不会起任何作用,相当于直接操作了target
。
当我们写了某个trap
以后,在做对应的动作时,就会触发我们的回调函数,由我们来控制被代理对象的行为。
最常用的两个trap
应该就是get
和set
了。
早年JavaScript
有着在定义对象时针对某个属性进行设置getter
、setter
:
let obj = { _age: 18, get age () { return `I'm ${this._age} years old` }, set age (val) { this._age = Number(val) } } console.log(obj.age) // I'm 18 years old obj.age = 19 console.log(obj.age) // I'm 19 years old
就像这段代码描述的一样,我们设置了一个属性_age
,然后又设置了一个get age
和set age
。
然后我们可以直接调用obj.age
来获取一个返回值,也可以对其进行赋值。
这么做有几个缺点:
针对每一个要代理的属性都要编写对应的getter
、setter
。
必须还要存在一个存储真实值的key
(如果我们直接在getter
里边调用this.age
则会出现堆栈溢出的情况,因为无论何时调用this.age
进行取值都会触发getter
)。
Proxy
很好的解决了这两个问题:
let target = { age: 18, name: 'Niko Bellic' } let handlers = { get (target, property) { return `${property}: ${target[property]}` }, set (target, property, value) { target[property] = value } } let proxy = new Proxy(target, handlers) proxy.age = 19 console.log(target.age, proxy.age) // 19, age : 19 console.log(target.name, proxy.name) // Niko Bellic, name: Niko Bellic
我们通过创建get
、set
两个trap
来统一管理所有的操作,可以看到,在修改proxy
的同时,target
的内容也被修改,而且我们对proxy
的行为进行了一些特殊的处理。
而且我们无需额外的用一个key
来存储真实的值,因为我们在trap
内部操作的是target
对象,而不是proxy
对象。
因为在使用了Proxy
웨이보 계정을 개설한 매우 인기 있는 스타가 있다는 것은 이해할 수 있습니다. 이 계정은 팬들에게 답장을 보내고 어디에서나 좋아요를 보내는 등 매우 활발하지만 실제로는 그 사람이 관리하지 않을 수도 있습니다.
하지만 운영 뒤에는 또 다른 사람이나 팀이 있는데, 그들을 에이전트라고 부를 수 있습니다. 그들이 게시하는 웨이보는 스타 그 자체의 의미를 나타내기 때문입니다.
<a href="http:/%20/www.php.cn/wiki/48.html" target="_blank">JavaScript🎜</a>
, 이는 객체
또는 함수에 대한 프록시 작업으로 이해될 수 있습니다. 코드>. 🎜🎜Proxy in JavaScript🎜🎜Proxy는 ES6에서 제공되는 새로운 API로, 객체의 다양한 기본 작업에 대한 사용자 정의 동작을 정의하는 데 사용할 수 있습니다🎜<em>(문서에서는 <code>트랩
이라고 함) 객체의 다양한 동작을 대상으로 하는 후크로 이해될 수 있습니다.) 🎜이를 사용하면 많은 흥미로운 작업을 수행할 수 있으며 일부 객체의 동작을 제어해야 할 때 매우 효과적입니다. 🎜🎜프록시 구문🎜🎜프록시
인스턴스를 생성하려면 두 개의 매개변수를 전달해야 합니다🎜target
개체 프록시 대상은 객체
또는 함수
🎜handler
일 수 있습니다. 프록시 객체에 대한 다양한 매개변수 작업 동작 처리🎜 (() => { let target = {} let handlers = { get: (target, property) => { target[property] = (property in target) ? target[property] : {} if (typeof target[property] === 'object') { return new Proxy(target[property], handlers) } return target[property] } } let proxy = new Proxy(target, handlers) console.log('z' in proxy.x.y) // false (其实这一步已经针对`target`创建了一个x.y的属性) proxy.x.y.z = 'hello' console.log('z' in proxy.x.y) // true console.log(target.x.y.z) // hello })()
trap
가 정의되어 있지 않으면> > 존재하지 않습니다. 이는 target
을 직접 조작하는 것과 같습니다. 🎜특정 트랩
을 작성하고 해당 작업을 수행하면 콜백 함수🎜로 프록시 개체의 동작을 제어합니다. 🎜🎜가장 일반적으로 사용되는 두 가지 trap
은 아마도 get
과 set
일 것입니다. 🎜초창기에는 JavaScript
가 객체를 정의할 때 특정 속성에 대해 getter
및 setter
를 설정하는 데 사용되었습니다. 🎜class Test { constructor (a, b) { console.log('constructor', a, b) } } // Test(1, 2) // throw an error let proxyClass = new Proxy(Test, { apply (target, thisArg, argumentsList) { // 如果想要禁止使用非new的方式来调用函数,直接抛出异常即可 // throw new Error(`Function ${target.name} cannot be invoked without 'new'`) return new (target.bind(thisArg, ...argumentsList))() } }) proxyClass(1, 2) // constructor 1 2
_age
속성을 설정한 다음 연령 가져오기
및 연령 설정
을 설정합니다. 🎜그런 다음 obj.age
를 직접 호출하여 반환 값을 얻거나 할당할 수 있습니다. 🎜이 작업에는 몇 가지 단점이 있습니다. 🎜getter
및 setter가 다음과 같아야 합니다.
로 작성되었습니다. 🎜key
도 있어야 합니다(getter
에서 this.age를 직접 호출하는 경우). >
값을 얻기 위해 this.age
가 호출될 때마다 getter
)가 트리거되기 때문에 스택 오버플로가 발생합니다. 🎜Proxy
는 다음 두 가지 문제를 매우 잘 해결합니다. 🎜function add (a, b) { return a + b } let proxy = new Proxy(add, { construct (target, argumentsList, newTarget) { throw new Error(`Function ${target.name} cannot be invoked with 'new'`) } }) proxy(1, 2) // 3 new proxy(1, 2) // throw an error
get
, set
두 개의 trap
을 사용하여 모든 작업을 균일하게 관리합니다. proxy
가 수정되면 target
의 내용도 수정되는 것을 볼 수 있으며, 몇 가지 특별한 변경 사항을 적용했습니다. 프록시
의 동작에 영향을 미칩니다. 🎜그리고 실제 값을 저장하기 위해 추가 키
를 사용할 필요가 없습니다. 프록시객체. 🎜🎜Proxy로 할 일🎜🎜Proxy
를 사용하고 나면 기본적으로 개체의 동작을 제어할 수 있기 때문에 이전에는 구현하기 더 복잡했던 작업을 이를 사용하여 수행할 수 있습니다. 🎜아래에는 몇 가지 간단하게 적용 가능한 시나리오가 나열되어 있습니다. 🎜在一些层级比较深的对象属性获取中,如何处理undefined
一直是一个痛苦的过程,如果我们用Proxy
可以很好的兼容这种情况。
(() => { let target = {} let handlers = { get: (target, property) => { target[property] = (property in target) ? target[property] : {} if (typeof target[property] === 'object') { return new Proxy(target[property], handlers) } return target[property] } } let proxy = new Proxy(target, handlers) console.log('z' in proxy.x.y) // false (其实这一步已经针对`target`创建了一个x.y的属性) proxy.x.y.z = 'hello' console.log('z' in proxy.x.y) // true console.log(target.x.y.z) // hello })()
我们代理了get
,并在里边进行逻辑处理,如果我们要进行get
的值来自一个不存在的key
,则我们会在target
中创建对应个这个key
,然后返回一个针对这个key
的代理对象。
这样就能够保证我们的取值操作一定不会抛出can not get xxx from undefined
但是这会有一个小缺点,就是如果你确实要判断这个key
是否存在只能够通过in
操作符来判断,而不能够直接通过get
来判断。
如果我们提供了一个Class
对象给其他人,或者说一个ES5
版本的构造函数。
如果没有使用new
关键字来调用的话,Class
对象会直接抛出异常,而ES5
中的构造函数this
指向则会变为调用函数时的作用域。
我们可以使用apply
这个trap
来兼容这种情况:
class Test { constructor (a, b) { console.log('constructor', a, b) } } // Test(1, 2) // throw an error let proxyClass = new Proxy(Test, { apply (target, thisArg, argumentsList) { // 如果想要禁止使用非new的方式来调用函数,直接抛出异常即可 // throw new Error(`Function ${target.name} cannot be invoked without 'new'`) return new (target.bind(thisArg, ...argumentsList))() } }) proxyClass(1, 2) // constructor 1 2
我们使用了apply
来代理一些行为,在函数调用时会被触发,因为我们明确的知道,代理的是一个Class
或构造函数,所以我们直接在apply
中使用new
关键字来调用被代理的函数。
以及如果我们想要对函数进行限制,禁止使用new
关键字来调用,可以用另一个trap
:construct
function add (a, b) { return a + b } let proxy = new Proxy(add, { construct (target, argumentsList, newTarget) { throw new Error(`Function ${target.name} cannot be invoked with 'new'`) } }) proxy(1, 2) // 3 new proxy(1, 2) // throw an error
在前端发送请求,我们现在经常用到的应该就是fetch
了,一个原生提供的API。
我们可以用Proxy
来包装它,使其变得更易用。
let handlers = { get (target, property) { if (!target.init) { // 初始化对象 ['GET', 'POST'].forEach(method => { target[method] = (url, params = {}) => { return fetch(url, { headers: { 'content-type': 'application/json' }, mode: 'cors', credentials: 'same-origin', method, ...params }).then(response => response.json()) } }) } return target[property] } } let API = new Proxy({}, handlers) await API.GET('XXX') await API.POST('XXX', { body: JSON.stringify({name: 1}) })
对GET
、POST
进行了一层封装,可以直接通过.GET
这种方式来调用,并设置一些通用的参数。
写过测试的各位童鞋,应该都会知道断言这个东西 console.assert
就是一个断言工具,接受两个参数,如果第一个为false
,则会将第二个参数作为Error message
抛出。
我们可以使用Proxy
来做一个直接赋值就能实现断言的工具。
let assert = new Proxy({}, { set (target, message, value) { if (!value) console.error(message) } }) assert['Isn\'t true'] = false // Error: Isn't true assert['Less than 18'] = 18 >= 19 // Error: Less than 18
在做服务端时,我们可以用Proxy
代理一些函数,来统计一段时间内调用的次数。
在后期做性能分析时可能会能够用上:
function orginFunction () {} let proxyFunction = new Proxy(orginFunction, { apply (target, thisArg. argumentsList) { log(XXX) return target.apply(thisArg, argumentsList) } })
这里列出了handlers
所有可以定义的行为 (traps):
Descripting | |
---|---|
의 몇 가지 예를 볼 수 있습니다. | 특정 키를 설정하세요 값key 值 |
set | 设置某个key 值 |
has | 使用in 操作符判断某个key 是否存在 |
apply | 函数调用,仅在代理对象为function 时有效 |
ownKeys | 获取目标对象所有的key
|
construct | 函数通过实例化调用,仅在代理对象为function 时有效 |
isExtensible | 判断对象是否可扩展,Object.isExtensible 的代理 |
deleteProperty | 删除一个property
|
defineProperty | 定义一个新的property
|
특정 | |
| 함수 호출, 다음 경우에만 유효합니다. 프록시 객체는|
대상 객체의 모든 | |
함수가 인스턴스화됩니다. 호출되며, 다음 경우에만 유효합니다. 프록시 개체는 개체가 확장 가능한지 확인하고 | deleteProperty |
isExtensible
defineProperty🎜getPrototypeOf🎜🎜프로토타입 개체 가져오기🎜🎜🎜🎜setPrototypeOf🎜🎜프로토타입 개체 설정🎜 🎜 🎜🎜preventExtensions🎜🎜객체를 확장 불가능하도록 설정🎜🎜🎜🎜getOwnPropertyDescriptor🎜🎜자체 소유 속성의 속성 설명 가져오기🎜(프로토타입 체인으로 검색하지 않고)🎜🎜🎜🎜🎜🎜믿습니다 이 기사의 사례를 읽은 후 방법을 마스터했습니다. 더 흥미로운 내용을 보려면 PHP 중국어 웹사이트의 다른 관련 기사를 주목하세요! 🎜🎜추천 도서: 🎜🎜🎜 HTML에서 JS 메서드 사용 요약 🎜🎜🎜🎜🎜 CSS 선택기 사용 방법 요약 🎜🎜🎜위 내용은 프록시 사용 사례에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!