Proxy API学习

Posted seven

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Proxy API学习相关的知识,希望对你有一定的参考价值。

handler.apply(target, thisArg, argumentsList)

用于拦截函数的调用。

target:目标对象(函数)。
thisArg:被调用时的上下文对象。
argumentsList:被调用时的参数数组。

apply方法可以返回任何值。

如果违反了以下约束,代理将抛出一个TypeError:
target必须是可被调用的。也就是说,它必须是一个函数对象。

handler.construct(target, argumentsList, newTarget)

用于拦截new 操作符. 为了使new操作符在生成的Proxy对象上生效,用于初始化代理的目标对象自身必须具有[[Construct]]内部方法(即 new target 必须是有效的)。

target:目标对象。
argumentsList:constructor的参数列表。
newTarget:最初被调用的构造函数。

construct 方法必须返回一个对象。

如果违反以下约定,代理将会抛出错误 TypeError:
必须返回一个对象.

var p = new Proxy(function() {}, {
  construct: function(target, argumentsList, newTarget) {
    return 1;
  }
});

new p(); // TypeError is thrown

handler.defineProperty(target, property, descriptor)

用于拦截对对象的 Object.defineProperty() 操作

target:目标对象。
property:待检索其描述的属性名。
descriptor:待定义或修改的属性的描述符。

如果违背了以下的不变量,proxy会抛出 TypeError:

  1. 如果目标对象不可扩展, 将不能添加属性。
  2. 不能添加或者修改一个属性为不可配置的,如果它不作为一个目标对象的不可配置的属性存在的话。
  3. 如果目标对象存在一个对应的可配置属性,这个属性可能不会是不可配置的。
  4. 如果一个属性在目标对象中存在对应的属性,那么 Object.defineProperty(target, prop, descriptor) 将不会抛出异常。
  5. 在严格模式下, false 作为 handler.defineProperty 方法的返回值的话将会抛出 TypeError 异常.

当调用 Object.defineProperty() 或者 Reflect.defineProperty(),传递给defineProperty的descriptor有一个限制 - 只有以下属性才有用,非标准的属性将会被无视 :

enumerable
configurable
writable
value
get
set

var p = new Proxy({}, {
  defineProperty(target, prop, descriptor) {
    console.log(descriptor);
    return Reflect.defineProperty(target, prop, descriptor);
  }
});

Object.defineProperty(p, \'name\', {
  value: \'proxy\',
  type: \'custom\'
});  // { value: \'proxy\' }

handler.deleteProperty(target, property)

用于拦截对对象属性的 delete 操作

target:目标对象。
property:待删除的属性名。

deleteProperty 必须返回一个Boolean类型的值,表示了该属性是否被成功删除。

如果违背了以下不变量,proxy 将会抛出一个 TypeError:

  1. 如果目标对象的属性是不可配置的,那么该属性不能被删除。

handler.get(target, property, receiver)

用于拦截对象的读取属性操作。

target:目标对象。
property:被获取的属性名。
receiver:Proxy或者继承Proxy的对象

如果违背了以下的约束,proxy会抛出 TypeError:

  1. 如果要访问的目标属性是不可写以及不可配置的,则返回的值必须与该目标属性的值相同。
  2. 如果要访问的目标属性没有配置访问方法,即get方法是undefined的,则返回值必须为undefined。
var obj = {};
Object.defineProperty(obj, "a", {
  configurable: false,
  enumerable: false,
  value: 10,
  writable: false
});

var p = new Proxy(obj, {
  get: function(target, prop) {
    return 20;
  }
});

p.a; //会抛出TypeError

handler.getOwnPropertyDescriptor(target, prop)

Object.getOwnPropertyDescriptor()的钩子。

target:目标对象。
prop:返回属性名称的描述。

getOwnPropertyDescriptor 方法必须返回一个object或undefined。

如果下列不变量被违反,代理将抛出一个 TypeError:

  1. getOwnPropertyDescriptor 必须返回一个 object 或 undefined。
  2. 如果属性作为目标对象的不可配置的属性存在,则该属性无法报告为不存在。
  3. 如果属性作为目标对象的属性存在,并且目标对象不可扩展,则该属性无法报告为不存在。
  4. 如果属性不存在作为目标对象的属性,并且目标对象不可扩展,则不能将其报告为存在。
  5. 属性不能被报告为不可配置,如果它不作为目标对象的自身属性存在,或者作为目标对象的可配置的属性存在。
  6. Object.getOwnPropertyDescriptor(target)的结果可以使用 Object.defineProperty 应用于目标对象,也不会抛出异常。

handler.getPrototypeOf(target)

是一个代理(Proxy)方法,当读取代理对象的原型时,该方法就会被调用。

getPrototypeOf 方法的返回值必须是一个对象或者null。

javascript中,下面这五种操作(方法/属性/运算符)可以触发JS引擎读取一个对象的原型,也就是可以触发 getPrototypeOf() 代理方法的运行:

  1. Object.getPrototypeOf()
  2. Reflect.getPrototypeOf()
  3. proto
  4. Object.prototype.isPrototypeOf()
  5. instanceof

如果遇到了下面两种情况,JS 引擎会抛出 TypeError 异常:

  1. getPrototypeOf() 方法返回的不是对象也不是 null。
  2. 目标对象是不可扩展的,且getPrototypeOf()方法返回的原型不是目标对象本身的原型。

5种触发getPrototypeOf代理方法的方式

var obj = {};
var p = new Proxy(obj, {
    getPrototypeOf(target) {
        return Array.prototype;
    }
});
console.log(
    Object.getPrototypeOf(p) === Array.prototype,  // true
    Reflect.getPrototypeOf(p) === Array.prototype, // true
    p.__proto__ === Array.prototype,               // true
    Array.prototype.isPrototypeOf(p),              // true
    p instanceof Array                             // true
);

两种情况下的异常

var obj = {};
var p = new Proxy(obj, {
    getPrototypeOf(target) {
        return "foo";
    }
});
Object.getPrototypeOf(p); // TypeError: "foo" is not an object or null

var obj = Object.preventExtensions({});
var p = new Proxy(obj, {
    getPrototypeOf(target) {
        return {};
    }
});
Object.getPrototypeOf(p); // TypeError: expected same prototype value

handler.has(target, prop)

是针对 in 操作符的代理方法。

has方法返回一个boolean属性的值.

如果违反了下面这些规则, proxy将会抛出TypeError:

  1. 如果目标对象的某一属性本身不可被配置,则该属性不能够被代理隐藏.
  2. 如果目标对象为不可扩展对象,则该对象的属性不能够被代理隐藏

handler.isExtensible(target)

handler.isExtensible() 方法用于拦截对对象的Object.isExtensible()。

isExtensible方法必须返回一个 Boolean值或可转换成Boolean的值。

如果违背了以下的约束,proxy会抛出 TypeError:

  1. Object.isExtensible(proxy) 必须同Object.isExtensible(target)返回相同值。也就是必须返回true或者为true的值,返回false和为false的值都会报错。
var p = new Proxy({}, {
  isExtensible: function(target) {
    return false;//return 0;return NaN等都会报错
  }
});

Object.isExtensible(p); // TypeError is thrown

handler.ownKeys(target)

用于拦截 Reflect.ownKeys().

ownKeys方法必须返回一个可枚举对象.

该拦截器可以拦截以下操作::

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

如果违反了下面的约束,proxy将抛出错误 TypeError:

  1. ownKeys 的结果必须是一个数组.
  2. 数组的元素类型要么是一个 String ,要么是一个 Symbol.
  3. 结果列表必须包含目标对象的所有不可配置(non-configurable )、自有(own)属性的key.
  4. 如果目标对象不可扩展,那么结果列表必须包含目标对象的所有自有(own)属性的key,不能有其它值.

handler.preventExtensions(target)

用于设置对Object.preventExtensions()的拦截

preventExtensions方法返回一个布尔值.

这个trap可以拦截这些操作:
Object.preventExtensions()
Reflect.preventExtensions()

如果违反了下列规则, proxy则会抛出一个 TypeError:

  1. 如果目标对象是可扩展的,那么只能返回false

handler.set(target, property, value, receiver)

以下是传递给 set() 方法的参数。this 绑定在 handler 对象上。

target:目标对象。
property:将被设置的属性名或 Symbol。
value:新属性值。
receiver:最初被调用的对象。通常是 proxy 本身,但 handler 的 set 方法也有可能在原型链上,或以其他方式被间接地调用(因此不一定是 proxy 本身)。

比如:假设有一段代码执行 obj.name = "jen", obj不是一个 proxy,且自身不含 name 属性,但是它的原型链上有一个 proxy,那么,那个 proxy 的 set() 处理器会被调用,而此时,obj 会作为 receiver 参数传进来。

set() 方法应当返回一个布尔值。

返回 true 代表属性设置成功。
在严格模式下,如果 set() 方法返回 false,那么会抛出一个 TypeError 异常。

如果违背以下的约束条件,proxy 会抛出一个 TypeError 异常:

  1. 若目标属性是一个不可写及不可配置的数据属性,则不能改变它的值。
  2. 如果目标属性没有配置存储方法,即 [[Set]] 属性的是 undefined,则不能设置它的值。
  3. 在严格模式下,如果 set() 方法返回 false,那么也会抛出一个 TypeError 异常。

handler.setPrototypeOf(target, prototype)

主要用来拦截 Object.setPrototypeOf().

如果成功修改了[[Prototype]], setPrototypeOf 方法返回 true,否则返回 false.

如果违反了下列规则,则proxy将抛出一个TypeError:

  1. 如果 target 不可扩展, 原型参数必须与Object.getPrototypeOf(target) 的值相同.
  2. 如果你不想为你的对象设置一个新的原型,你的handler\'s的setPrototypeOf方法可以返回false,也可以抛出异常。

以上是关于Proxy API学习的主要内容,如果未能解决你的问题,请参考以下文章

手写一个基于 Proxy 的缓存库

html 通过表单向bcls-proxy提交API请求的示例代码

onActivityResult 未在 Android API 23 的片段上调用

openGL之API学习(一六七)默认着色器 顶点属性索引 别名索引

阿里四面:你知道Spring AOP创建Proxy的过程吗?

导航到另一个片段时触发 API 调用