【问题标题】:Array.prototype.forEach() not working when called on a proxy with a get handlerArray.prototype.forEach() 在使用 get 处理程序的代理上调用时不起作用
【发布时间】:2017-03-17 11:24:45
【问题描述】:

我有以下代理:

const p = new Proxy({
  [Symbol.iterator]: Array.prototype.values,
  forEach: Array.prototype.forEach,
}, {
  get(target, property) {
    if (property === '0') return 'one';
    if (property === '1') return 'two';
    if (property === 'length') return 2;
    return Reflect.get(target, property);
  },
});

它是一个类似数组的对象,因为它具有数值属性和指定元素数量的length 属性。我可以使用for...of 循环对其进行迭代:

for (const element of p) {
  console.log(element); // logs 'one' and 'two'
}

但是,forEach() 方法不起作用。

p.forEach(element => console.log(element));

此代码不记录任何内容。永远不会调用回调函数。为什么它不起作用,我该如何解决?

代码sn-p:

const p = new Proxy({
  [Symbol.iterator]: Array.prototype.values,
  forEach: Array.prototype.forEach,
}, {
  get(target, property) {
    if (property === '0') return 'one';
    if (property === '1') return 'two';
    if (property === 'length') return 2;
    return Reflect.get(target, property);
  },
});

console.log('for...of loop:');
for (const element of p) {
  console.log(element);
}

console.log('forEach():');
p.forEach(element => console.log(element));
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.16.0/polyfill.min.js"></script>

【问题讨论】:

  • 为什么会遭到反对?
  • 我不认为这个问题的标题特别具有描述性。如果你们中的任何人有更好的标题的想法,请随时编辑。
  • @Pointy 我注意到在其他此类问题中也是如此 - 似乎有些用户不喜欢自我回答的问题。不过,不知道为什么,但我看到其他人在没有评论的情况下投了反对票。至于标题,我同意它可以改进 - 一开始我并不确定它的含义。阅读问题后,我认为标题非常适合......但我不知道如何使它在第一次阅读时更具描述性。
  • 您可以扩展 Array 以不填充每个在被代理时可能会损坏的功能。
  • @estus 我可以,但这并不能解决问题。

标签: javascript foreach ecmascript-6 iteration es6-proxy


【解决方案1】:

for...of 循环和Array.prototype.forEach() 之间的区别之一是前者使用@@iterator 属性循环对象,而后者将属性从0 迭代到length,并执行仅当对象具有该属性时回调。它使用[[HasProperty]] 内部方法,在这种情况下,它为每个数组元素返回false

解决方案是同时添加has() 处理程序,它将拦截[[HasProperty]] 调用。

工作代码:

const p = new Proxy({
  [Symbol.iterator]: Array.prototype.values,
  forEach: Array.prototype.forEach,
}, {
  get(target, property) {
    if (property === '0') return 'one';
    if (property === '1') return 'two';
    if (property === 'length') return 2;
    return Reflect.get(target, property);
  },
  has(target, property) {
    if (['0', '1', 'length'].includes(property)) return true;
    return Reflect.has(target, property);
  },
});

p.forEach(element => console.log(element));
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.16.0/polyfill.min.js"></script>

【讨论】:

    【解决方案2】:

    还有一个相对简单的附加选项。使用Array.from() 生成一个可以迭代的数组。

    const a = Array.from(p);
    a.forEach(element => console.log(element));
    

    完整代码:

    const p = new Proxy({
      [Symbol.iterator]: Array.prototype.values,
      forEach: Array.prototype.forEach,
    }, {
      get(target, property) {
        if (property === '0') return 'one';
        if (property === '1') return 'two';
        if (property === 'length') return 2;
        return Reflect.get(target, property);
      },
    });
    
    const a = Array.from(p);
    a.forEach(element => console.log(element));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多