We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proxy对象是用于包装另一个对象,并拦截其读/写等操作
Proxy
const p = new Proxy(target, handler);
target
handler
get
[[Get]]
set
[[Set]]
has
[[HasProperty]]
in
deleteProperty
[[Delete]]
delete
apply
[[Call]]
ownKeys
[[OwnPropertyKeys]]
Object.getOwnPropertyNames
Object.getOwnPropertySymbols
getOwnPropertyDescriptor
[[GetOwnProperty]]
Object.getOwnPropertyDescriptor
const p = new Proxy(target, { get: function (target, property, receiver) {}, });
undefined
0
let arr = [1, 2, 3]; arr = new Proxy(arr, { get(target, p) { if (p in target) { return target[p]; } return 0; }, }); console.log(arr[1]); console.log(arr[10]);
总结:借助get钩子可以在读取一个对象上不存在的属性时,得到一个默认值
const p = new Proxy(target, { set: function (target, property, value, receiver) {}, });
注意:如果写入成功则返回true,否则返回false(触发TypeError)
true
false
TypeError
let arr = []; arr = new Proxy(arr, { set(target, p, value) { // 拦截写入操作 if (typeof value == 'number') { target[p] = value; return true; } return false; }, }); arr.push(1); arr.push('2');
总结:借助set钩子可以对一个对象的属性和值进行验证
const p = new Proxy(target, { has: function (target, prop) {}, });
range
let range = [1, 10]; range = new Proxy(range, { has(target, p) { return p >= target[0] && p <= target[1]; }, }); console.log(5 in range); console.log(100 in range);
const p = new Proxy(target, { ownKeys: function (target) {}, });
for...in
Object.keys()
_
let obj = { name: 'jianwu', age: 24, _password: '123456', }; obj = new Proxy(obj, { ownKeys(target) { return Object.keys(target).filter((key) => !key.startsWith('_')); }, }); for (let key in obj) console.log(key); console.log(Object.keys(obj)); console.log(Object.values(obj));
注意:如果ownKeys钩子返回对象上不存在的属性,Object.getOwnPropertyNames方法可以列出不存在的键;但Object.keys不可以,因为Object.keys方法只返回带有enumerable标记的非Symbol键,可以使用getOwnPropertyDescriptor钩子将enumerable标记改为true,就可列出了。
Object.keys
enumerable
Symbol
const p = new Proxy(target, { deleteProperty: function (target, property) {}, });
let obj = { name: 'jianwu', age: 24, _password: '123456', }; obj = new Proxy(obj, { deleteProperty(target, p) { if (p.startsWith('_')) { throw new Error('拒绝访问'); } delete target[p]; return true; }, }); delete obj._password;
const p = new Proxy(target, { apply: function (target, thisArg, argumentsList) {}, });
delay(fn, ms)
ms
fn
function delay(fn, ms) { // 返回一个调用 fn 函数的包装器 return function () { setTimeout(() => fn.apply(this, arguments), ms); }; } function sayHi(name) { console.log('Hello ' + name); } console.log(sayHi.length); sayHi = delay(sayHi, 3000); console.log(sayHi.length); sayHi('jianwu');
下面使用Proxy来包装上面的函数:
function delay(fn, ms) { return new Proxy(fn, { apply(target, thisArg, args) { setTimeout(() => target.apply(thisArg, args), ms); }, }); } function sayHi(name) { console.log('Hello ' + name); } console.log(sayHi.length); sayHi = delay(sayHi, 3000); console.log(sayHi.length); sayHi('jianwu');
总结:普通的包装函数不会转发读写等操作,所以无法访问到原始函数的属性,如length, name等;而Proxy则可以在代理对象上的所有操作转发到原始函数,从而实现一个更完整的包装器
length
name
Reflect对象提供拦截JS操作的方法,与Proxy handler的方法相同,可以简化创建Proxy
Reflect
JS
Proxy handler
内部方法仅在规范中使用,不能直接调用,Reflect的方法对内部方法进行包装,使得调用成为可能
Reflect.get(target, propertyKey, receiver)
target[name]
Reflect.set(target, propertyKey, value, receiver)
target[name] = value
Reflect.has(target, propertyKey)
name in target
Reflect.deleteProperty(target, propertyKey)
delete target[name]
let user = { _name: 'jianwu', get name() { return this._name; }, }; let proxy = new Proxy(user, { get(target, p, receiver) { return target[p]; }, }); console.log(user.name); // admin 继承 user 后,admin.name 是什么? let admin = { _name: 'admin', __proto__: proxy, }; console.log(admin.name);
上面的栗子中,触发get钩子会从原对象返回target[p],此处属性是一个getter访问器,this指向原对象,所以返回user.name。
target[p]
getter
this
user.name
对于普通函数,可以使用call/apply来绑定正确的this,但是getter怎么绑定呢?
call/apply
Reflect.get
let proxy = new Proxy(user, { get(target, p, receiver) { // receiver -> admin return Reflect.get(target, p, receiver); }, });
给定一个数组:arr = [1, 2, 3],实现:arr[-1] = 3,arr[-2] = 2,arr[-3] = 1
arr = [1, 2, 3]
arr[-1] = 3
arr[-2] = 2
arr[-3] = 1
arr = new Proxy(arr, { get(target, p, receiver) { if (p < 0) { p = target.length + Number(p); } return Reflect.get(...arguments); }, });
创建一个makeObservable(target)函数,使得对象可观察
makeObservable(target)
function makeObservable(target) { let handlers = []; target.observe = function (handler) { handlers.push(handler); }; return new Proxy(target, { set(target, p, value, receiver) { const res = Reflect.set(...arguments); if (res) { handlers.forEach((handler) => handler(p, value)); } return res; }, }); } let user = {}; user = makeObservable(user); user.observe((key, value) => { console.log(key + ' = ' + value); }); user.name = 'wjw'; user.age = 24;
The text was updated successfully, but these errors were encountered:
No branches or pull requests
Proxy
Proxy
对象是用于包装另一个对象,并拦截其读/写等操作语法
target
: 要包装的目标对象(任何类型的对象,包括函数)handler
: 带有钩子函数的对象,用于处理代理对象的各种拦截操作,下面列出一些常用的操作方法:get
[[Get]]
set
[[Set]]
has
[[HasProperty]]
in
操作符deleteProperty
[[Delete]]
delete
操作符apply
[[Call]]
ownKeys
[[OwnPropertyKeys]]
Object.getOwnPropertyNames
Object.getOwnPropertySymbols
getOwnPropertyDescriptor
[[GetOwnProperty]]
Object.getOwnPropertyDescriptor
get 钩子
undefined
,可以利用代理,使得访问不存在的项返回0
总结:借助
get
钩子可以在读取一个对象上不存在的属性时,得到一个默认值set 钩子
注意:如果写入成功则返回
true
,否则返回false
(触发TypeError
)总结:借助
set
钩子可以对一个对象的属性和值进行验证has 钩子
range
范围内ownKeys 钩子
for...in
或Object.keys()
遍历对象,并过滤_
开头的属性注意:如果
ownKeys
钩子返回对象上不存在的属性,Object.getOwnPropertyNames
方法可以列出不存在的键;但Object.keys
不可以,因为Object.keys
方法只返回带有enumerable
标记的非Symbol
键,可以使用getOwnPropertyDescriptor
钩子将enumerable
标记改为true
,就可列出了。deleteProperty 钩子
_
开头的属性apply 钩子
delay(fn, ms)
方法,在ms
后执行fn
函数下面使用
Proxy
来包装上面的函数:总结:普通的包装函数不会转发读写等操作,所以无法访问到原始函数的属性,如
length
,name
等;而Proxy
则可以在代理对象上的所有操作转发到原始函数,从而实现一个更完整的包装器Reflect
Reflect
对象提供拦截JS
操作的方法,与Proxy handler
的方法相同,可以简化创建Proxy
内部方法仅在规范中使用,不能直接调用,
Reflect
的方法对内部方法进行包装,使得调用成为可能Reflect.get(target, propertyKey, receiver)
target[name]
[[Get]]
Reflect.set(target, propertyKey, value, receiver)
target[name] = value
[[Set]]
Reflect.has(target, propertyKey)
name in target
[[HasProperty]]
Reflect.deleteProperty(target, propertyKey)
delete target[name]
[[Delete]]
getter 代理
Proxy
的get
钩子返回代理对象原属性上面的栗子中,触发
get
钩子会从原对象返回target[p]
,此处属性是一个getter
访问器,this
指向原对象,所以返回user.name
。对于普通函数,可以使用
call/apply
来绑定正确的this
,但是getter
怎么绑定呢?Reflect.get
案例
负数索引访问数组
给定一个数组:
arr = [1, 2, 3]
,实现:arr[-1] = 3
,arr[-2] = 2
,arr[-3] = 1
实现简单的 Observable
创建一个
makeObservable(target)
函数,使得对象可观察The text was updated successfully, but these errors were encountered: