ES6 中的 Proxy 对象详解

阅读时长 18 分钟读完

ES6 中的 Proxy 对象是一种可以在对象访问中拦截、更改或扩展行为的工具。通过 Proxy 对象,我们可以更加灵活地处理对象的属性访问、方法调用、构造器调用等操作。本文将详细介绍 Proxy 对象的使用方法和特性,并给出相关示例代码。

Proxy 对象的基本用法

创建一个 Proxy 对象,需要定义一个 Target 对象和一个 Handler 对象,其中 Target 对象为被代理的对象,Handler 对象为拦截器,用于拦截、处理 Target 对象的各种操作。以下为创建 Proxy 对象的代码示例:

-- -------------------- ---- -------
--- ------ - -
    ----- -------
    ---- --
--

--- ------- - -
    ---- -------- ----- --------- -- -
        -------------------- ----------
        ------ -------------
    --

    ---- -------- ----- ------ --------- -- -
        -------------------- ------- -- -----------
        ------------ - ------
        ------ -----
    -
--

--- ----- - --- ------------- ---------

在以上代码中,我们定义了一个 Target 对象 target,包含 nameage 两个属性。同时,我们定义了一个 Handler 对象 handler,其包含了两个拦截器:get 拦截器用于处理属性访问操作,set 拦截器用于处理属性设置操作。最后,我们使用 new Proxy() 方法创建了 Proxy 对象 proxy

通过以上代码,我们可以在 proxy 对象上进行属性的访问和设置,同时拦截器可以捕捉到相关操作并做出相应的处理。

例如,我们可以使用以下代码访问 proxy 对象中的 name 属性:

执行以上代码,会先输出 "Getting name",表示访问了 name 属性,然后输出 "Lucy",表示返回了 target.name 属性的值。相应地,我们也可以使用以下代码来设置 proxy 对象的 name 属性:

执行以上代码,会先输出 "Setting name to Lily",表示设置了 name 属性的值为 "Lily"

Proxy 对象的拦截器类型

除了 getset 拦截器,我们还可以使用 Proxy 对象提供的其他拦截器,包括:applyconstructdeletePropertyhasgetOwnPropertyDescriptordefinePropertygetPrototypeOfsetPrototypeOfisExtensiblepreventExtensionsownKeys 等。以下是拦截器的介绍和使用方法:

apply 拦截器

apply 拦截器用于拦截函数调用,比如 func(...args)func.apply(receiver, args)。其接收三个参数:

  • target:被代理的函数;
  • thisArg:函数调用时的 this 值;
  • argArray:函数调用时传入的参数数组。

以下是一个 apply 拦截器的代码示例:

-- -------------------- ---- -------
--- ---- - --------- -- -
    -------------------- ---- ---- ------- ------
    ------ ----------------- -- -- --- - -- ---
--

--- ------- - -
    ------ -------- -------- --------- -- -
        -------------------- -------- ---- ------- ----------
        ------ --------------------
    -
--

--- ----- - --- ----------- ---------

-------------------- -- -- -- ---- -- -- --

在以上代码中,我们定义了一个函数 func,接收多个参数并返回它们的和。我们还定义了一个 apply 拦截器,拦截了 proxy 对象的函数调用,输出传入的参数并调用 target(...argArray) 方法。

construct 拦截器

construct 拦截器用于拦截构造函数的调用,例如 new func(...args)。其接收两个参数:

  • target:被代理的构造函数;
  • argArray:构造函数调用时传入的参数数组。

以下是一个 construct 拦截器的代码示例:

-- -------------------- ---- -------
----- ------ -
    ----------------- ---- -
        --------- - -----
        -------- - ----
    -

    ---------- -
        ------------------- -- ---- -- ------------- --- ----------- ----- -------
    -
-

--- ------- - -
    ---------- -------- --------- -- -
        --- ------ ---- - ---------
        --------------------- --- ------ ---- ---- ------- --- --- ----------
        ------ --- ------------ -----
    -
--

--- ----- - --- ------------- ---------

--- ------ - --- ------------- ----
------------------ -- -- ------- -- ---- -- ----- --- -- ----- -----

在以上代码中,我们定义了一个 Person 类,包含 nameage 两个属性,和 sayHello() 方法。我们还定义了一个 construct 拦截器,拦截了 proxy 对象的构造调用,输出传入的参数并调用 target(...argArray) 方法。

deleteProperty 拦截器

deleteProperty 拦截器用于拦截删除一个属性的操作,例如 delete obj.prop。其接收两个参数:

  • target:被代理的对象;
  • prop:要删除的属性名。

以下是一个 deleteProperty 拦截器的代码示例:

-- -------------------- ---- -------
--- --- - -
    ----- -------
    ---- --
--

--- ------- - -
    --------------- -------- ----- -- -
        --------------------- -------- ----------
        ------ -------------
        ------ -----
    -
--

--- ----- - --- ---------- ---------

------ ---------- -- -- --------- -------- ----
------------------- -- -- ------ -------

在以上代码中,我们定义了一个对象 obj,包含 nameage 两个属性。我们还定义了一个 deleteProperty 拦截器,拦截了 proxy 对象的删除属性操作,输出被删除的属性名并调用 delete target[prop] 方法。

has 拦截器

has 拦截器用于拦截属性是否存在的操作,例如 'prop' in obj。其接收两个参数:

  • target:被代理的对象;
  • prop:属性名。

以下是一个 has 拦截器的代码示例:

-- -------------------- ---- -------
--- --- - -
    ----- -------
    ---- --
--

--- ------- - -
    ---- -------- ----- -- -
        --------------------- -- -------- ------- ---------
        ------ ---- -- -------
    -
--

--- ----- - --- ---------- ---------

------------------ -- ------- -- -- --------- -- -------- ---- ----------- ----
-------------------- -- ------- -- -- --------- -- -------- ------ ----------- -----

在以上代码中,我们定义了一个对象 obj,包含 nameage 两个属性。我们还定义了一个 has 拦截器,拦截了 proxy 对象的检查属性是否存在的操作,输出被检查的属性名并调用 prop in target 方法。

getOwnPropertyDescriptor 拦截器

getOwnPropertyDescriptor 拦截器用于拦截获取属性描述符的操作,例如 Object.getOwnPropertyDescriptor(obj, 'prop')。其接收两个参数:

  • target:被代理的对象;
  • prop:属性名。

以下是一个 getOwnPropertyDescriptor 拦截器的代码示例:

-- -------------------- ---- -------
--- --- - -
    ----- -------
    ---- --
--

--- ------- - -
    ------------------------- -------- ----- -- -
        -------------------- -------- ---------- -- ----------
        ------ --------------------------------------- ------
    -
--

--- ----- - --- ---------- ---------

-------------------------------------------------- --------- -- -- -------- -------- ---------- -- --------- ------- ------- --------- ----- ----------- ----- ------------- -----

在以上代码中,我们定义了一个对象 obj,包含 nameage 两个属性。我们还定义了一个 getOwnPropertyDescriptor 拦截器,拦截了 proxy 对象的获取属性描述符的操作,输出被获取的属性名并调用 Object.getOwnPropertyDescriptor(target, prop) 方法。

defineProperty 拦截器

defineProperty 拦截器用于拦截定义一个属性的操作,例如 Object.defineProperty(obj, 'prop', {value: 123})。其接收三个参数:

  • target:被代理的对象;
  • prop:要定义的属性名;
  • desc:要定义的属性描述符对象。

以下是一个 defineProperty 拦截器的代码示例:

-- -------------------- ---- -------
--- --- - -
    ----- -------
    ---- --
--

--- ------- - -
    --------------- -------- ----- ----- -- -
        --------------------- -------- ------- ---- ------------- ------
        ------ ----------------------------- ----- ------
    -
--

--- ----- - --- ---------- ---------

---------------------------- --------- ------- ----------- -- -- --------- -------- ------ ---- ----------- ------- --------- --------- ------ ----------- ------ ------------- -------
------------------- -- -- ------ ------- ---- --- ------- ---------

在以上代码中,我们定义了一个对象 obj,包含 nameage 两个属性。我们还定义了一个 defineProperty 拦截器,拦截了 proxy 对象的定义属性的操作,输出被定义的属性名和属性描述符并调用 Object.defineProperty(target, prop, desc) 方法。

getPrototypeOf 拦截器

getPrototypeOf 拦截器用于拦截获取一个对象的原型的操作,例如 Object.getPrototypeOf(obj)。其接收一个参数:

  • target:被代理的对象。

以下是一个 getPrototypeOf 拦截器的代码示例:

-- -------------------- ---- -------
--- --- - -
    ----- -------
    ---- --
--

--- ------- - -
    --------------- -------- -- -
        -------------------- --------- -- ---------
        ------ ------------------------------
    -
--

--- ----- - --- ---------- ---------

------------------------------------------ -- -- -------- --------- -- ----------- ----------------

在以上代码中,我们定义了一个对象 obj,包含 nameage 两个属性。我们还定义了一个 getPrototypeOf 拦截器,拦截了 proxy 对象的获取原型的操作,输出被代理的对象并调用 Object.getPrototypeOf(target) 方法。

setPrototypeOf 拦截器

setPrototypeOf 拦截器用于拦截设置一个对象的原型的操作,例如 Object.setPrototypeOf(obj, proto)。其接收两个参数:

  • target:被代理的对象;
  • proto:要设置的新原型对象。

以下是一个 setPrototypeOf 拦截器的代码示例:

-- -------------------- ---- -------
--- --- - -
    ----- -------
    ---- --
--

--- ------- - -
    --------------- -------- ------ -- -
        -------------------- --------- -- ------ -- -----------
        ------ ----------------------------- -------
    -
--

--- ----- - --- ---------- ---------

---------------------------- ----
------------------------------------------ -- -- -------- --------

在以上代码中,我们定义了一个对象 obj,包含 nameage 两个属性。我们还定义了一个 setPrototypeOf 拦截器,拦截了 proxy 对象的设置原型的操作,输出被代理的对象和新的原型对象并调用 Object.setPrototypeOf(target, proto) 方法。

isExtensible 拦截器

isExtensible 拦截器用于拦截检测一个对象是否可扩展的操作,例如 Object.isExtensible(obj)。其接收一个参数:

  • target:被代理的对象。

以下是一个 isExtensible 拦截器的代码示例:

-- -------------------- ---- -------
--- --- - -
    ----- -------
    ---- --
--

--- ------- - -
    ------------- -------- -- -
        --------------------- -- ------ -- -------------
        ------ ----------------------------
    -
--

--- ----- - --- ---------- ---------

---------------------------------------- -- -- --------- -- ------ -- --------------- ----

在以上代码中,我们定义了一个对象 obj,包含 nameage 两个属性。我们还定义了一个 isExtensible 拦截器,拦截了 proxy 对象的检测是否可扩展的操作,输出被代理的对象并调用 Object.isExtensible(target) 方法。

preventExtensions 拦截器

preventExtensions 拦截器用于拦截使一个对象不可扩展的操作,例如 Object.preventExtensions(obj)。其接收一个参数:

  • target:被代理的对象。

以下是一个 preventExtensions 拦截器的代码示例:

-- -------------------- ---- -------
--- --- - -
    ----- -------
    ---- --
--

--- ------- - -
    ------------------ -------- -- -
        ----------------------- ------ ---- ----- -----------
        ------ ---------------------------------
    -
--

--- ----- - --- ---------- ---------

--------------------------------
---------------------------------------- -- -- ----------- ------ ---- ----- ------------- -----

在以上代码中,我们定义了一个对象 obj,包含 nameage 两个属性。我们还定义了一个 preventExtensions 拦截器,拦截了 proxy 对象的使其不可扩展的操作,输出被代理的对象并调用 Object.preventExtensions(target) 方法。

ownKeys 拦截器

ownKeys 拦截器用于拦截获取一个对象自身属性键名的操作,例如 Object.getOwnPropertyNames(obj)Object.getOwnPropertySymbols(obj)Reflect.ownKeys(obj)。其接收一个参数:

  • target:被代理的对象。

以下是一个 ownKeys 拦截器的代码示例:

-- -------------------- ---- -------
--- --- - -
    ----- -------
    ---- --
--

--- ------- - -
    -------- -------- -- -
        -------------------- --- -------- ---- -- ---------
        ------ -----------------------------------
    -
--

--- ----- - --- ---------- ---------

----------------------------------------------- -- -- -------- --- -------- ---- -- ----------- -------- ------

在以上代码中,我们定义了一个对象 obj,包含 nameage 两个属性。我们还定义了一个 ownKeys 拦截器,拦截了 proxy 对象的获取其自身属性键名的操作,输出被代理的对象并调用 Object.getOwnPropertyNames(target) 方法。

Proxy 对象的符号键

除了上述拦截器类型,Proxy 对象还支持一些特殊的符号键,用于进行属性访问拦截、代理的相关操作。以下是常用的几种符号键:

  • get(target, prop, receiver):拦截对象的属性读取操作;
  • set(target, prop, value, receiver):拦截对象的属性设置操作;
  • has(target, prop):拦截 in 操作符;
  • deleteProperty(target, prop):拦截 delete 操作符;
  • apply(target, thisArg, args):拦截函数调用;
  • construct(target, args):拦截构造函数调用。

其它符号键的使用方法和用途,请参考 MDN 文档。

总结

通过本文介绍,我们了解了 ES6 中的 Proxy 对象的基本用法和特性。Proxy 对象的拦截器类型和符号键,可以让我们更加灵活地处理对象的属性访问、方法调用、构造器调用等操作,为我们的编码提供了很大的方便和开发效率。经过深入的学习和练习,我们可以更好地掌握 Proxy 对象的使用方法,并在实际的项目开发中灵活运用。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/65a99fa4add4f0e0ff2fd314

纠错
反馈