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 // xiaomind.name = 'sdf';d.name // xiaomin
es5
for (let [key] of Object.entries(0)) {Object.defineProperty(o, key, {writable: false})}o.price = 300o.price // 190
撤销 Proxy
let d = Proxy.revocable(o, {})d.proxy.price // 190d.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) // 1new fproxy(1, 2) // {value: 2}fproxy.prototype === Object.prototype // truefproxy.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() // falseproxy.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>## 使用```javascriptMath.floor.apply(null,[3.99]) // 3// 运行时Reflect.apply(Math.floor,null,[3.99]) //3
反射有什么用
Object 提供了部分反射的功能,
Object 有的,Reflect 都有,reflect 有 Object 没有的
动态传方法
大于100
let price = 101.5if (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"}) // trueReflect.deleteProperty(student, 'name') //falseObject.getOwnPropertyDescriptor(student, 'name')Reflect.getOwnPropertyDescriptor(student, 'name')delete student.name //false// 必须configurable:true,configurable 默认为 falsevar obj = {a:1}delete obj.a // trueReflect.deleteProperty(obj, 'a') // true// 提供了一种读取属性的方法Reflect.get(student, 'name') // zbjReflect.get([3,4],1) // 4Reflect.has(obj, 'a')Object.has(obj, 'a') //errorReflect.isExtensible(obj) // trueObject.freeze(obj)// 或者Reflect.preventExtensions(obj)obj.z = 3Reflect.isExtensible(obj) // trueReflect.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
