Proxy

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

使用

  1. let o = {
  2. name: 'xiaomin',
  3. price: 190
  4. }
  5. // d 中介
  6. let d = new Proxy(o, {
  7. get (target, key) {
  8. if (key === 'price') {
  9. return target[key] + 20
  10. } else {
  11. return target[key]
  12. }
  13. }
  14. })
  15. d.price // 210

Proxy 实际上重载(overload)了点运算符,上面代码对一个o 对象架设了一层拦截,重定义了属性的读取(get)和设置(set)行为

如果handler没有设置任何拦截,那就等同于直接通向原对象。

  1. var target = {};
  2. var handler = {};
  3. var proxy = new Proxy(target, handler);
  4. proxy.a = 'b';
  5. target.a // "b"

一个技巧是将 Proxy 对象,设置到object.proxy属性,从而可以在object对象上调用。

  1. var object = { proxy: new Proxy(target, handler) };

Proxy 实例也可以作为其他对象的原型对象。

  1. var proxy = new Proxy({}, {
  2. get: function(target, propKey) {
  3. return 35;
  4. }
  5. });
  6. let obj = Object.create(proxy);
  7. obj.time // 35

通过中介不能修改,但是中介自己可以

  1. let d = new Proxy(o, {
  2. get (target, key) {
  3. return target[key]
  4. },
  5. set (target, key, value) {
  6. return false
  7. }
  8. })
  9. d.name // xiaomin
  10. d.name = 'sdf';
  11. d.name // xiaomin

es5

  1. for (let [key] of Object.entries(0)) {
  2. Object.defineProperty(o, key, {
  3. writable: false
  4. })
  5. }
  6. o.price = 300
  7. o.price // 190

撤销 Proxy

  1. let d = Proxy.revocable(o, {})
  2. d.proxy.price // 190
  3. d.revoke()
  4. d.proxy.price // Cannot perform 'get' on a proxy that has been revoked
  1. var handler = {
  2. get: function(target, name) {
  3. if (name === 'prototype') {
  4. return Object.prototype;
  5. }
  6. return 'Hello, ' + name;
  7. },
  8. apply: function(target, thisBinding, args) {
  9. return args[0];
  10. },
  11. construct: function(target, args) {
  12. return {value: args[1]};
  13. }
  14. };
  15. var fproxy = new Proxy(function(x, y) {
  16. return x + y;
  17. }, handler);
  18. fproxy(1, 2) // 1
  19. new fproxy(1, 2) // {value: 2}
  20. fproxy.prototype === Object.prototype // true
  21. fproxy.foo === "Hello, foo" // true

this 问题

虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理。

  1. const target = {
  2. m: function () {
  3. console.log(this === proxy);
  4. }
  5. };
  6. const handler = {};
  7. const proxy = new Proxy(target, handler);
  8. target.m() // false
  9. proxy.m() // true

Proxy 实例的方法

下面是 Proxy 支持的拦截操作一览,一共 13 种。

  • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']
  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['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

  1. class Com {
  2. constructor () {
  3. this.tid = Math.random().toString(36).slice(-8)
  4. }
  5. get id () {
  6. return this.tid
  7. }
  8. }
  9. let com1 = new Com()
  10. let com2 = new Com()
  11. for (let i = 0; i < 10; i++) {
  12. console.log(com1.id,com2.id)
  13. }
  14. com1.id = 1;
  15. console.log(com1.id,com2.id)
  1. class Com {
  2. constructor () {
  3. this.proxy = new Proxy (
  4. {id: Math.random().toString(36).slice(-8)},
  5. {}
  6. )
  7. }
  8. get id () {
  9. return this.proxy.id
  10. }
  11. }
  12. let com1 = new Com()
  13. let com2 = new Com()
  14. for (let i = 0; i < 10; i++) {
  15. console.log(com1.id,com2.id)
  16. }
  17. com1.id = 1;
  18. console.log(com1.id,com2.id)

使用 proxy如何校验

  1. window.addEventListener('error', (e) => {
  2. console.log(e.message)
  3. report('./')
  4. },true)
  5. let validator = (target, key, value) => {
  6. if (Reflect.has(target, key)) {
  7. return false
  8. }
  9. if (key === 'price') {
  10. if (value > 300) {
  11. throw new TypeError('price exceed 300')
  12. // return false
  13. } else {
  14. target[key] = value
  15. }
  16. } else {
  17. target[key] = value
  18. }
  19. }
  20. let d = new Proxy(o, {
  21. get (target, key) {
  22. return target[key] || ''
  23. },
  24. set: validator
  25. })
  26. d.price = 301

Reflect

反射机制

java 的放射机制是再编译阶段不知道是哪个类被加载,而是在运行的时候才加载,执行

Reflect 对象的设计目的

  • Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上
  • 修改某些Object方法的返回结果,让其变得更合理。

比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false

  1. // 老写法
  2. try {
  3. Object.defineProperty(target, property, attributes);
  4. // success
  5. } catch (e) {
  6. // failure
  7. }
  8. // 新写法
  9. if (Reflect.defineProperty(target, property, attributes)) {
  10. // success
  11. } else {
  12. // failure
  13. }
  • Object操作都变成函数行为。某些Object操作是命令式,比如name in objdelete obj[name],而Reflect.has(obj, name)Reflect.deleteProperty(obj, name)让它们变成了函数行为。 ```javascript // 老写法 ‘assign’ in Object // true

// 新写法 Reflect.has(Object, ‘assign’) // true

  1. - `Reflect`对象的方法与`Proxy`对象的方法一一对应,只要是`Proxy`对象的方法,就能在`Reflect`对象上找到对应的方法。这就让`Proxy`对象可以方便地调用对应的`Reflect`方法,完成默认行为,作为修改行为的基础。也就是说,不管`Proxy`怎么修改默认行为,你总可以在`Reflect`上获取默认行为。
  2. <a name="Buc0K"></a>
  3. ## 使用
  4. ```javascript
  5. Math.floor.apply(null,[3.99]) // 3
  6. // 运行时
  7. Reflect.apply(Math.floor,null,[3.99]) //3

反射有什么用

Object 提供了部分反射的功能,
Object 有的,Reflect 都有,reflect 有 Object 没有的

动态传方法

大于100

  1. let price = 101.5
  2. if (price > 100) {
  3. price = Math.floor.apply(null,[price])
  4. } else {
  5. price = Math.ceil.apply(null,[price])
  6. }
  7. // reflect 做法
  8. Reflect.apply(price > 100 ? Math.floor : Math.ceil,null,[price])

类的实例

  1. let d = new Date()
  2. d.getTime()
  3. Reflect.getPrototypeOf(d)
  4. let d = Reflect.construct(Date, [])
  5. d.getTime()
  6. d instanceof Date

动态添加属性

  1. const student = {}
  2. // 返回值不一样
  3. Object.defineProperty(student, 'name', {value: "zbj"}) // {name: zbj}
  4. Reflect.defineProperty(student, 'name', {value: "zbj"}) // true
  5. Reflect.deleteProperty(student, 'name') //false
  6. Object.getOwnPropertyDescriptor(student, 'name')
  7. Reflect.getOwnPropertyDescriptor(student, 'name')
  8. delete student.name //false
  9. // 必须configurable:true,configurable 默认为 false
  10. var obj = {a:1}
  11. delete obj.a // true
  12. Reflect.deleteProperty(obj, 'a') // true
  13. // 提供了一种读取属性的方法
  14. Reflect.get(student, 'name') // zbj
  15. Reflect.get([3,4],1) // 4
  16. Reflect.has(obj, 'a')
  17. Object.has(obj, 'a') //error
  18. Reflect.isExtensible(obj) // true
  19. Object.freeze(obj)
  20. // 或者
  21. Reflect.preventExtensions(obj)
  22. obj.z = 3
  23. Reflect.isExtensible(obj) // true
  24. Reflect.ownKeys(obj)
  25. Reflect.ownKeys([]) // ['length']
  26. Object.keys([]) // []
  27. Reflect.set(obj, 'x', 4)
  28. const arr = [1,2,3]
  29. Reflect.set(arr,2,'new 3') // [1, 2, "new 3"]
  30. Reflect.setPrototypeOf(arr, String.prototype)
  31. arr.sort() // error

代理数据读取

todo