最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 学习JavaScript -- 代理和反射

    正文概述 掘金(前端一条余)   2021-02-07   462

    代理基础

    代理是es6为开发者提供的可以拦截住针对对象的一些底层操作,并且额外在这基础上面添加一些其他的行为。 它相当于一个看门的,拦住来客(底层操作)问话(开发者逻辑)。行就开门进去,不行就打一顿丢出去

    创建代理

    const handler = {}
    const target = {
      id: 'target'
    }
    const proxy = new Proxy(target, handler)
    

    代理是由Proxy构造函数创建出来的一个代理对象,构造函数接收两个参数

    • target 目标对象(要操作的对象)
    • handler 处理程序对象(拦截后触发的捕获器api的集合对象)

    目标对象可以直接调用,我们也可以通过代理对象进行调用。最终的操作结果也会反映到目标对象里面

    const handler = {}
    const target = {
      id: 'target'
    }
    const proxy = new Proxy(target, handler)
    proxy.id = 'proxy'
    // 修改代理对象的属性值,也会反映到目标对象
    console.log(target) // { id: "proxy" }
    console.log(proxy) // { id: 'proxy' }
    // 再目标对象里面操作,同样也会反映到代理对象
    // 因为引用的是同一个对象
    traget.name = 'yu'
    console.log(target) // { id: "proxy", name: 'yu' }
    console.log(proxy) // { id: "proxy", name: 'yu' }
    

    注:Proxy构造函数的prototype是undefined

    捕获器

    代理之所以能够针对底层操作进行拦截并且能够进行额外的操作,就是通过捕获器来实现的。捕获器是定义再处理程序对象里面的,一个处理程序对象可以定义多个捕获器方法。每个捕获器都对应着一个基本操作。

    • 比如获取对象属性的操作,对应着get捕获器方法
    const handler = {
      get(target, property, proxy) {
      	console.log('触发了get方法')
        return target[property]
      }
    }
    const target = {
      id: 'target'
    }
    const proxy = new Proxy(target, handler)
    proxy.id // '触发了get方法'
    

    这些捕获器只在代理对象触发,直接使用目标对象是不会触发的

    捕获器方法都有对应的参数,通过这些参数可以重新构建新的行为逻辑。比如get方法就会有三个参数

    • target 目标对象
    • property 要查询的属性
    • proxy 代理对象

    一个代理里面包含着13种捕获器方法,这些捕获器方法可以重新构造新的行为。但是也要遵守一个就是要有这个操作的原始行为,但是有些原始行为十分复制,我们手动如法炮制可能很麻烦。所以es6给开发者提供了反射API,他给我们提供了基本的原始行为不需要我们来写。

    反射(Reflect)

    反射对象是一个有13个方法的对象,相当于一个Object的子集。但是它将Object的一些命令式的操作转变成了函数式。

    • 比如 in操作符 这个就对应着Reflect.has
    • 比如 delete操作符 这个就对应着Reflect.deleteProperty

    反射对象还有一个好处就是修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false

    reflect对象的最大作用是和proxy代理对象的捕获器的方法是一一对应的,可以结合proxy代理对象一起使用

    const target = {
      id: 'target'
    }
    const handler = {
      get(target, property, handler) {
          let a = ''
          if (property === 'id') {
              a = '!!!'
          }
          return Reflect.get(...arguments) + a
      }
    }
    const proxy = new Proxy(target, handler)
    console.log(proxy.id) // target!!!
    

    捕获器API

    get()

    get捕获器是再获取属性的时候触发的,对应的反射api是Reflect.get

    get方法的返回值是没有限制的

    拦截的操作

    • proxy.property
    • proxy[property]
    • Object.create(proxy)[property]
    • Reflect.get(proxy, property, receiver)

    参数

    • target 目标对象
    • property 要查询的属性
    • proxy 代理对象
    const myTarget = {}; 
    const proxy = new Proxy(myTarget, { 
     get(target, property, receiver) { 
     console.log('get()'); 
     return Reflect.get(...arguments) 
     } 
    }); 
    proxy.foo; 
    // get()
    

    set()

    set捕获器是通过设置属性的时候触发的,对应的反射api是Reflect.set

    set方法的返回值 返回 true 表示成功;返回 false 表示失败,严格模式下会抛出 TypeError

    拦截操作

    • proxy.property = value
    • proxy[property] = value
    • Object.create(proxy)[property] = value
    • Reflect.set(proxy, property, value, receiver)

    参数

    • target:目标对象。
    • property:引用的目标对象上的字符串键属性。
    • value:要赋给属性的值。
    • receiver:接收最初赋值的对象。
    const myTarget = {}; 
    const proxy = new Proxy(myTarget, { 
     set(target, property, value, receiver) { 
     console.log('set()'); 
     return Reflect.set(...arguments) 
     } 
    }); 
    proxy.foo = 'bar'; 
    // set()
    

    has()

    has捕获器是通过使用了in操作符触发的,对应的反射api是Reflect.has()

    has方法必须返回布尔值,表示属性是否存在。返回非布尔值会被转型为布尔值

    拦截操作

    • property in proxy
    • property in Object.create(proxy)
    • with(proxy) {(property);}
    • Reflect.has(proxy, property)

    参数

    • target:目标对象。
    • property:引用的目标对象上的字符串键属性。
    const myTarget = {}; 
    const proxy = new Proxy(myTarget, { 
     has(target, property) { 
     console.log('has()'); 
     return Reflect.has(...arguments) 
     } 
    }); 
    'foo' in proxy; 
    // has()
    

    defineProperty()

    defineProperty捕获器是通过使用了Object.defineProperty()中被触发,对应的反射api是Reflect.defineProperty()

    defineProperty方法必须返回布尔值,表示属性是否成功定义。返回非布尔值会被转型为布尔值。

    拦截操作

    • Object.defineProperty(proxy, property, descriptor)
    • Reflect.defineProperty(proxy, property, descriptor)

    参数

    • target:目标对象。
    • property:引用的目标对象上的字符串键属性。
    • descriptor:包含可选的 enumerable、configurable、writable、value、get 和 set定义的对象。
    const myTarget = {}; 
    const proxy = new Proxy(myTarget, { 
     defineProperty(target, property, descriptor) { 
     console.log('defineProperty()'); 
     return Reflect.defineProperty(...arguments) 
     } 
    }); 
    Object.defineProperty(proxy, 'foo', { value: 'bar' }); 
    // defineProperty()
    

    getOwnPropertyDescriptor()

    getOwnPropertyDescriptor捕获器是通过使用了Object.getOwnPropertyDescriptor()中触发的,对应的反射api是Reflect.getOwnPropertyDescriptor()

    getOwnPropertyDescriptor方法必须返回对象,或者在属性不存在时返回 undefined。

    拦截操作

    • Object.getOwnPropertyDescriptor(proxy, property)
    • Reflect.getOwnPropertyDescriptor(proxy, property)

    参数

    • target:目标对象。
    • property:引用的目标对象上的字符串键属性。
    const myTarget = {}; 
    const proxy = new Proxy(myTarget, { 
     getOwnPropertyDescriptor(target, property) { 
     console.log('getOwnPropertyDescriptor()'); 
     return Reflect.getOwnPropertyDescriptor(...arguments) 
     } 
    }); 
    Object.getOwnPropertyDescriptor(proxy, 'foo'); 
    // getOwnPropertyDescriptor()
    

    deleteProperty()

    deleteProperty捕获器是通过使用了delete操作符触发的,对应的反射api是Reflect.deleteProperty()

    deleteProperty方法必须返回布尔值,表示删除属性是否成功。返回非布尔值会被转型为布尔值。

    拦截操作

    • delete proxy.property
    • delete proxy[property]
    • Reflect.deleteProperty(proxy, property)

    参数

    • target:目标对象。
    • property:引用的目标对象上的字符串键属性。
    const myTarget = {}; 
    const proxy = new Proxy(myTarget, { 
     deleteProperty(target, property) { 
     console.log('deleteProperty()'); 
     return Reflect.deleteProperty(...arguments) 
     } 
    }); 
    delete proxy.foo 
    // deleteProperty()
    

    ownKeys()

    ownKeys捕获器是通过Object.keys()及类似方法中被触发。对应的反射 API 方法为 Reflect.ownKeys()

    拦截操作

    • Object.getOwnPropertyNames(proxy)
    • Object.getOwnPropertySymbols(proxy)
    • Object.keys(proxy)
    • Reflect.ownKeys(proxy)

    参数

    • target:目标对象。
    const myTarget = {}; 
    const proxy = new Proxy(myTarget, { 
     ownKeys(target) { 
     console.log('ownKeys()'); 
     return Reflect.ownKeys(...arguments) 
     } 
    }); 
    Object.keys(proxy); 
    // ownKeys()
    

    getPrototypeOf()

    getPrototypeOf捕获器是通过在 Object.getPrototypeOf()获取原型对象中被触发的,对应的Reflect.getPrototypeOf()

    getPrototypeOf()必须返回对象或 null。

    拦截操作

    • Object.getPrototypeOf(proxy)
    • Reflect.getPrototypeOf(proxy)
    • proxy.proto
    • Object.prototype.isPrototypeOf(proxy)
    • proxy instanceof Object

    参数

    • target:目标对象。
    const myTarget = {}; 
    const proxy = new Proxy(myTarget, { 
     getPrototypeOf(target) { 
     console.log('getPrototypeOf()'); 
     return Reflect.getPrototypeOf(...arguments) 
     } 
    }); 
    Object.getPrototypeOf(proxy); 
    // getPrototypeOf()
    

    setPrototypeOf()

    setPrototypeOf捕获器是通过使用Object.setPrototypeOf()设置原型对象中触发的,对应的Reflect.setPrototypeOf()

    setPrototypeOf方法必须返回布尔值,表示原型赋值是否成功。返回非布尔值会被转型为布尔值

    拦截操作

    • Object.setPrototypeOf(proxy)
    • Reflect.setPrototypeOf(proxy)

    参数

    • target:目标对象。
    • prototype:target 的替代原型,如果是顶级原型则为 null。
    const myTarget = {}; 
    const proxy = new Proxy(myTarget, { 
     setPrototypeOf(target, prototype) { 
     console.log('setPrototypeOf()'); 
     return Reflect.setPrototypeOf(...arguments) 
     } 
    }); 
    Object.setPrototypeOf(proxy, Object); 
    // setPrototypeOf()
    

    isExtensible

    isExtensible捕获器的通过Object.isExtensible()是否可扩展中被调用。对应的反射 API 方法为Reflect.isExtensible()

    isExtensible()必须返回布尔值,表示 target 是否可扩展。返回非布尔值会被转型为布尔值。

    拦截操作

    • Object.isExtensible(proxy)
    • Reflect.isExtensible(proxy)

    参数

    • target:目标对象。
    const myTarget = {}; 
    const proxy = new Proxy(myTarget, { 
     isExtensible(target) { 
     console.log('isExtensible()');
     return Reflect.isExtensible(...arguments) 
     } 
    }); 
    Object.isExtensible(proxy); 
    // isExtensible()
    

    preventExtensions

    preventExtensions捕获器会在 Object.preventExtensions()中被调用。对应的反射 API方法为 Reflect.preventExtensions()。

    preventExtensions()必须返回布尔值,表示 target 是否已经不可扩展。返回非布尔值会被转型为布尔值

    拦截操作

    • Object.preventExtensions(proxy)
    • Reflect.preventExtensions(proxy)

    参数

    • target:目标对象。
    const myTarget = {}; 
    const proxy = new Proxy(myTarget, { 
     preventExtensions(target) { 
     console.log('preventExtensions()'); 
     return Reflect.preventExtensions(...arguments) 
     } 
    }); 
    Object.preventExtensions(proxy); 
    // preventExtensions()
    

    apply

    apply捕获器会在调用函数时中被调用。对应的反射 API 方法为 Reflect.apply()

    拦截操作

    • proxy(...argumentsList)
    • Function.prototype.apply(thisArg, argumentsList)
    • Function.prototype.call(thisArg, ...argumentsList)
    • Reflect.apply(target, thisArgument, argumentsList)

    参数

    • target:目标对象。
    • thisArg:调用函数时的 this 参数。
    • argumentsList:调用函数时的参数列表
    const myTarget = () => {}; 
    const proxy = new Proxy(myTarget, { 
     apply(target, thisArg, ...argumentsList) { 
     console.log('apply()'); 
     return Reflect.apply(...arguments) 
     } 
    }); 
    proxy(); 
    // apply()
    

    construct

    construct()捕获器会在 new 操作符中被调用。对应的反射 API 方法为 Reflect.construct()

    construct()必须返回一个对象

    拦截操作

    • new proxy(...argumentsList)
    • Reflect.construct(target, argumentsList, newTarget)

    参数

    • target:目标构造函数。
    • argumentsList:传给目标构造函数的参数列表。
    • newTarget:最初被调用的构造函数。
    const myTarget = function() {}; 
    const proxy = new Proxy(myTarget, { 
     construct(target, argumentsList, newTarget) { 
     console.log('construct()'); 
     return Reflect.construct(...arguments) 
     } 
    }); 
    new proxy; 
    // construct()
    

    起源地下载网 » 学习JavaScript -- 代理和反射

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元