ホームページ > 記事 > ウェブフロントエンド > JavaScriptのProxyについて詳しく解説
まず、プロキシ
の意味を理解する必要があります。翻訳すると、この単語は次のようになります。 演技を意味します。
非常に人気のあるスターが Weibo アカウントを開設したことがわかります。このアカウントはファンに返信したり、どこにでも「いいね」を付けたりするなど、非常にアクティブですが、実際には彼によって管理されていない可能性があります. .
しかし、この作戦の背後には別の人物またはチームが存在しており、彼らをエージェントと呼ぶことができます。なぜなら、彼らが公開する微博はスター自身の意味を表しているからです。
P.S. スターを追っているわけではないので強引に例を挙げますが、そういう運営チームもあるかもしれないとの推測です
これを JavaScript に代入します
、これは、object
または function
に対するプロキシ操作として理解できます。
プロキシは ES6 で提供される新しい API で、オブジェクトのさまざまな基本操作のカスタム動作を定義するために使用できます (ドキュメントで説明されています)。トラップと呼ばれ、オブジェクトのさまざまな動作のフックとして理解できると思います)、これを使用して多くの興味深いことができ、オブジェクトの動作を制御する必要がある場合に非常に効果的になります。
Proxy
のインスタンスを作成するには、2 つのパラメータを渡す必要があります
target
プロキシされるオブジェクトは、object
または function
handlers
プロキシ オブジェクトのさまざまな操作動作 処理 let target = {} let handlers = {} // do nothing let proxy = new Proxy(target, handlers) proxy.a = 123 console.log(target.a) // 123
2 番目のパラメータが空のオブジェクトの場合、基本的に最初のパラメータの浅いコピーとして理解できます
(プロキシは浅いコピーでなければなりません。深いコピーの場合は、エージェントの意味が失われます)
上記のサンプルコードのように、対応する trap が存在する場合、
が定義されていない場合、何の効果もありません。これは、target
を直接操作するのと同じです。
特定の trap
を作成し、対応するアクションを実行すると、コールバック関数がトリガーされ、プロキシ オブジェクトの動作を制御します。
最も一般的に使用される 2 つの 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
Like このコードでも同じことを説明しています。プロパティ _age
を設定し、次に get age
と set age
を設定します。
その後、obj.age
を直接呼び出して戻り値を取得するか、それを割り当てることができます。
これを行うには、いくつかの欠点があります。
getter
および setter
を記述する必要があります。 。 key
も必要です (ゲッター内で this.age を直接呼び出すと、 this を呼び出すたびにスタック オーバーフローが発生します。 age は値を取得するときに getter をトリガーします)。 Proxy
は、次の 2 つの問題をうまく解決します。
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
Two ## を作成します。 #trap を使用して、すべての操作を均一に管理します。
proxy を変更すると、
target の内容も変更され、
proxy# の動作が得られることがわかります。 ## には特殊な処理が施されています。 そして、
内で target
オブジェクトを操作するため、実際の値を保存するために追加の key
を使用する必要はありません。 proxy
オブジェクトではありません。
を使用すると、オブジェクトの動作は基本的に制御可能になるため、次の目的に使用できます。以前は実装がより複雑だったいくつかのことを実行します。 適用可能な簡単なシナリオをいくつか以下に示します。
未定義のオブジェクト プロパティの問題を解決する
に対処する方法は常に面倒なプロセスでした。 ##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## から取得されます。 # の場合、対応する key
を target
に作成し、この key
のプロキシ オブジェクトを返します。 これにより、値操作で
can not get xxx from unknown がスローされなくなります。
key が存在するかどうかは、
in
演算子を介してのみ判断でき、get
を介して直接判断することはできません。 <h3 id="普通函数与构造函数的兼容处理">普通函数与构造函数的兼容处理</h3>
<p>如果我们提供了一个<code>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):
具体的可以查看MDN-Proxy
里边同样有一些例子
traps | description |
---|---|
get | 获取某个key 值 |
set | 设置某个key 值 |
has | 使用in 操作符判断某个key 是否存在 |
apply | 函数调用,仅在代理对象为function 时有效 |
ownKeys | 获取目标对象所有的key
|
construct | 函数通过实例化调用,仅在代理对象为function 时有效 |
isExtensible | 判断对象是否可扩展,Object.isExtensible 的代理 |
deleteProperty | 删除一个property
|
defineProperty | 定义一个新的property
|
getPrototypeOf | 获取原型对象 |
setPrototypeOf | 设置原型对象 |
preventExtensions | 设置对象为不可扩展 |
getOwnPropertyDescriptor | 获取一个自有属性 (不会去原型链查找) 的属性描述 |
更多编程相关知识,请访问:编程学习网站!!
以上がJavaScriptのProxyについて詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。