javascript - 为数组正确构建 Javascript 代理集处理程序

标签 javascript arrays proxy

为数组构建 Javascript 代理的正确方法是什么,这样“set”处理程序就不会因为对数组的单个更改而被多次调用?

我的意思是:

我想在 Proxy 对象中包装一个简单的数组。当我希望将一个新值 push() 到此 Proxy 对象时,我希望运行“设置”处理程序。

问题在于像“set”这样的代理处理程序被多次调用,用于对一个数组进行一次操作。在某些情况下,处理这个问题似乎相当容易,但在其他情况下,一次调用修改包装在 Proxy 对象中的数组的情况下,set handler 至少被调用一次 every元素

假设我像这样创建了最简单的 Proxy 处理程序对象和 Proxy:

let proxyHandlerObj = {
  set:function(target,property,value,receiver) {
    console.log("Set handler is invoked on property:",property,"with value:",value);
    /*
     * Important and interesting things done here
     */
    return (target[property] = value);
  }
};

let proxyArray = new Proxy(["zero","one","two"],proxyHandlerObj);

此代理只是拦截对我的代理数组的“类似设置”的调用 并将消息写入控制台。现在,当我将一个新元素添加到我的 proxyArray 对象的末尾时:

proxyArray.push("三")

我会得到这样的东西:

Set handler is invoked on property: 3 with value: three
Set handler is invoked on property: length with value: 4

我看到设置处理程序被调用了两次:一次用于在数组中创建新元素,另一次用于设置数组的新长度属性。

好的,这个问题可以通过检查被操作的属性来解决。我见过设置属性做这样的事情:

set:function(target,property,value,receiver) {
  if(property!="length") {
    console.log("Set handler is invoked on property:",property,"with value:",value);
    /*
     * Important and interesting things done here
     */
  }
  return (target[property] = value);
}

proxyArray.push("three") 的相同调用将对除length 属性之外的所有属性执行重要的操作。这是因为我正在检查是否设置了 length 属性。这对我来说似乎没问题。

但是,假设我想简单地 splice() 我的数组中的一些东西?:

proxyArray.splice(0,1);

这会为数组中的每个元素生成一个“set”调用:

Set handler is invoked on property: 0 with value: one
Set handler is invoked on property: 1 with value: two
Set handler is invoked on property: 2 with value: three

这当然不是我想要的。我希望我的设置处理程序在“splice()”上运行一次,而不是三次。

此外,对于同一 splice() 操作多次触发“set”方法会产生非常讨厌的副作用。通过将“set”处理程序更改为此来查看数组的内容:

set:function(target,property,value,receiver) {
  if(property!="length") {
    console.log("Set handler is invoked on property:",property,"with value:",value);
    /*
     * Important and interesting things done here
     */
  }
  let result = (target[property] = value);
  console.log(JSON.stringify(target));
  return result;
}

将产生这个:

Set handler is invoked on property: 0 with value: one
["one","one","two","three"]
Set handler is invoked on property: 1 with value: two
["one","two","two","three"]
Set handler is invoked on property: 2 with value: three
["one","two","three","three"]
["one","two","three"]

因此,Javascript 将数组中的每个值向下移动,一次一个,然后弹出最后一个重复元素作为最后一步。您的设置处理程序必须构建为处理这种中间重复。

这似乎会产生讨厌和复杂的“设置”处理程序。

那么,构建环绕数组的 Javascript 代理的正确方法是什么,这样“set”处理程序就不会被多次调用,并且目标对象在该过程中是可靠的?

最佳答案

正在发生的事情的最终问题是代理正在显式处理 .splice() 调用(意思是,所有涉及从数组中拉出元素、重新索引该数组的操作,并且修改 length 属性将全部通过代理——如果您调用 proxyArray.splice() 是预期的行为,因为 splice 将是代理而不是底层数组)。

问题的解决方法是让splice“直通”到底层数组,从而绕过代理。为了做到这一点,我们需要向代理添加一个 get 陷阱,并在我们调用 splice 时进行监听,以便我们可以确保它发生在数组本身上。

const proxyHandlerObj = {
    set(tgt, prop, val, rcvr) {
        if (prop !== 'length') {
            /*
             * Important and interesting things done here
             */
        }
        return (tgt[prop] = val);
    },

    get(tgt, prop, rcvr) {
        if (prop === 'splice') {
            const origMethod = tgt[prop];

            return function (...args) {
                /*
                 * Do anything special here
                 */
                origMethod.apply(tgt, args);
            }
        }
        return tgt[prop];
    }
};

关于这个的一些事情:

  • 使用任何参数调用 proxyArray.splice() 将返回一个函数,该函数调用我们从原始数组中提取的方法(我们必须将它绑定(bind)到数组,因为我们正在调用它通过代理的方法意味着 this 将指向该代理,我们的行为将有所不同)所有参数都通过
  • 我建议您将希望set 陷阱执行的任何特殊逻辑放在此处;你可以在技术上做一些事情来手动调用 set 陷阱并触发你的逻辑,但最好在此处调用该逻辑以保持代码不那么复杂和更模块化
  • 这将完全绕过 set 陷阱,但代理会反射(reflect)您对底层数组的所有更改
  • 您可以更改 prop === 'splice',检查您想要“渗透”到原始对象的任何属性,因此 push()/shift()/等。可以用同样的方式处理

关于javascript - 为数组正确构建 Javascript 代理集处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45528463/

相关文章:

javascript - 选择滚动元素时jsTree滚动回顶部

javascript - (JavaScript) 随机文本输出 - 建议?

javascript - 在setAttribute中添加多个属性

c++ - 在 XY 点数组中找到三个 XY 点的最大面积

node.js - HTTP : failed to make connection to backend: 0. 0.0.0 - 套接字 js

javascript - 模态窗口不合理地变得模糊

Python 矩阵乘法行与示例代码

arrays - 将int映射到2D数组

javascript - Node -HTTP-代理错误

ssl - 不启用 ssl 代理时,Charles 如何查看域