Proxy
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
使用
let o = {
name: 'xiaomin',
price: 190
}
// d 中介
let d = new Proxy(o, {
get (target, key) {
if (key === 'price') {
return target[key] + 20
} else {
return target[key]
}
}
})
d.price // 210
Proxy 实际上重载(overload)了点运算符,上面代码对一个o 对象架设了一层拦截,重定义了属性的读取(get
)和设置(set
)行为
如果handler
没有设置任何拦截,那就等同于直接通向原对象。
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
target.a // "b"
一个技巧是将 Proxy 对象,设置到object.proxy
属性,从而可以在object
对象上调用。
var object = { proxy: new Proxy(target, handler) };
Proxy 实例也可以作为其他对象的原型对象。
var proxy = new Proxy({}, {
get: function(target, propKey) {
return 35;
}
});
let obj = Object.create(proxy);
obj.time // 35
通过中介不能修改,但是中介自己可以
let d = new Proxy(o, {
get (target, key) {
return target[key]
},
set (target, key, value) {
return false
}
})
d.name // xiaomin
d.name = 'sdf';
d.name // xiaomin
es5
for (let [key] of Object.entries(0)) {
Object.defineProperty(o, key, {
writable: false
})
}
o.price = 300
o.price // 190
撤销 Proxy
let d = Proxy.revocable(o, {})
d.proxy.price // 190
d.revoke()
d.proxy.price // Cannot perform 'get' on a proxy that has been revoked
var handler = {
get: function(target, name) {
if (name === 'prototype') {
return Object.prototype;
}
return 'Hello, ' + name;
},
apply: function(target, thisBinding, args) {
return args[0];
},
construct: function(target, args) {
return {value: args[1]};
}
};
var fproxy = new Proxy(function(x, y) {
return x + y;
}, handler);
fproxy(1, 2) // 1
new fproxy(1, 2) // {value: 2}
fproxy.prototype === Object.prototype // true
fproxy.foo === "Hello, foo" // true
this 问题
虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的this
关键字会指向 Proxy 代理。
const target = {
m: function () {
console.log(this === proxy);
}
};
const handler = {};
const proxy = new Proxy(target, handler);
target.m() // false
proxy.m() // true
Proxy 实例的方法
下面是 Proxy 支持的拦截操作一览,一共 13 种。
- get(target, propKey, receiver):拦截对象属性的读取,比如
proxy.foo
和proxy['foo']
。 - set(target, propKey, value, receiver):拦截对象属性的设置,比如
proxy.foo = v
或proxy['foo'] = v
,返回一个布尔值。 - has(target, propKey):拦截
propKey in proxy
的操作,返回一个布尔值。 - deleteProperty(target, propKey):拦截
delete proxy[propKey]
的操作,返回一个布尔值。 - ownKeys(target):拦截
Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
、for...in
循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()
的返回结果仅包括目标对象自身的可遍历属性。 - getOwnPropertyDescriptor(target, propKey):拦截
Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象。 - defineProperty(target, propKey, propDesc):拦截
Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一个布尔值。 - preventExtensions(target):拦截
Object.preventExtensions(proxy)
,返回一个布尔值。 - getPrototypeOf(target):拦截
Object.getPrototypeOf(proxy)
,返回一个对象。 - isExtensible(target):拦截
Object.isExtensible(proxy)
,返回一个布尔值。 - setPrototypeOf(target, proto):拦截
Object.setPrototypeOf(proxy, proto)
,返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。 - apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如
proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。 - construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如
new proxy(...args)
。
例子
组件初始化的时候赋值一个只读且随机的 id
class Com {
constructor () {
this.tid = Math.random().toString(36).slice(-8)
}
get id () {
return this.tid
}
}
let com1 = new Com()
let com2 = new Com()
for (let i = 0; i < 10; i++) {
console.log(com1.id,com2.id)
}
com1.id = 1;
console.log(com1.id,com2.id)
class Com {
constructor () {
this.proxy = new Proxy (
{id: Math.random().toString(36).slice(-8)},
{}
)
}
get id () {
return this.proxy.id
}
}
let com1 = new Com()
let com2 = new Com()
for (let i = 0; i < 10; i++) {
console.log(com1.id,com2.id)
}
com1.id = 1;
console.log(com1.id,com2.id)
使用 proxy如何校验
window.addEventListener('error', (e) => {
console.log(e.message)
report('./')
},true)
let validator = (target, key, value) => {
if (Reflect.has(target, key)) {
return false
}
if (key === 'price') {
if (value > 300) {
throw new TypeError('price exceed 300')
// return false
} else {
target[key] = value
}
} else {
target[key] = value
}
}
let d = new Proxy(o, {
get (target, key) {
return target[key] || ''
},
set: validator
})
d.price = 301
Reflect
反射机制
java 的放射机制是再编译阶段不知道是哪个类被加载,而是在运行的时候才加载,执行
Reflect 对象的设计目的
- 将
Object
对象的一些明显属于语言内部的方法(比如Object.defineProperty
),放到Reflect
对象上 - 修改某些
Object
方法的返回结果,让其变得更合理。
比如,Object.defineProperty(obj, name, desc)
在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)
则会返回false
。
// 老写法
try {
Object.defineProperty(target, property, attributes);
// success
} catch (e) {
// failure
}
// 新写法
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// failure
}
- 让
Object
操作都变成函数行为。某些Object
操作是命令式,比如name in obj
和delete obj[name]
,而Reflect.has(obj, name)
和Reflect.deleteProperty(obj, name)
让它们变成了函数行为。 ```javascript // 老写法 ‘assign’ in Object // true
// 新写法 Reflect.has(Object, ‘assign’) // true
- `Reflect`对象的方法与`Proxy`对象的方法一一对应,只要是`Proxy`对象的方法,就能在`Reflect`对象上找到对应的方法。这就让`Proxy`对象可以方便地调用对应的`Reflect`方法,完成默认行为,作为修改行为的基础。也就是说,不管`Proxy`怎么修改默认行为,你总可以在`Reflect`上获取默认行为。
<a name="Buc0K"></a>
## 使用
```javascript
Math.floor.apply(null,[3.99]) // 3
// 运行时
Reflect.apply(Math.floor,null,[3.99]) //3
反射有什么用
Object 提供了部分反射的功能,
Object 有的,Reflect 都有,reflect 有 Object 没有的
动态传方法
大于100
let price = 101.5
if (price > 100) {
price = Math.floor.apply(null,[price])
} else {
price = Math.ceil.apply(null,[price])
}
// reflect 做法
Reflect.apply(price > 100 ? Math.floor : Math.ceil,null,[price])
类的实例
let d = new Date()
d.getTime()
Reflect.getPrototypeOf(d)
let d = Reflect.construct(Date, [])
d.getTime()
d instanceof Date
动态添加属性
const student = {}
// 返回值不一样
Object.defineProperty(student, 'name', {value: "zbj"}) // {name: zbj}
Reflect.defineProperty(student, 'name', {value: "zbj"}) // true
Reflect.deleteProperty(student, 'name') //false
Object.getOwnPropertyDescriptor(student, 'name')
Reflect.getOwnPropertyDescriptor(student, 'name')
delete student.name //false
// 必须configurable:true,configurable 默认为 false
var obj = {a:1}
delete obj.a // true
Reflect.deleteProperty(obj, 'a') // true
// 提供了一种读取属性的方法
Reflect.get(student, 'name') // zbj
Reflect.get([3,4],1) // 4
Reflect.has(obj, 'a')
Object.has(obj, 'a') //error
Reflect.isExtensible(obj) // true
Object.freeze(obj)
// 或者
Reflect.preventExtensions(obj)
obj.z = 3
Reflect.isExtensible(obj) // true
Reflect.ownKeys(obj)
Reflect.ownKeys([]) // ['length']
Object.keys([]) // []
Reflect.set(obj, 'x', 4)
const arr = [1,2,3]
Reflect.set(arr,2,'new 3') // [1, 2, "new 3"]
Reflect.setPrototypeOf(arr, String.prototype)
arr.sort() // error
代理数据读取
todo