admin管理员组

文章数量:1244249

I have the following proxy:

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);
  },
});

It's an array-like object, because it has numeric properties and the length property specifying the amount of elements. I can iterate it using a for...of loop:

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

However, the forEach() method is not working.

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

This code doesn't log anything. The callback function is never called. Why isn't it working and how can I fix it?

Code snippet:

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=".16.0/polyfill.min.js"></script>

I have the following proxy:

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);
  },
});

It's an array-like object, because it has numeric properties and the length property specifying the amount of elements. I can iterate it using a for...of loop:

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

However, the forEach() method is not working.

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

This code doesn't log anything. The callback function is never called. Why isn't it working and how can I fix it?

Code snippet:

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./ajax/libs/babel-polyfill/6.16.0/polyfill.min.js"></script>

Share Improve this question asked Nov 3, 2016 at 18:35 Michał PerłakowskiMichał Perłakowski 92.6k30 gold badges163 silver badges186 bronze badges 2
  • You could just extend Array to not polyfill each and every feature that can bee broken when being proxified. – Estus Flask Commented Nov 3, 2016 at 18:56
  • @estus I could, but that doesn't fix it. – Michał Perłakowski Commented Nov 3, 2016 at 19:20
Add a ment  | 

3 Answers 3

Reset to default 8

One of the differences between a for...of loop and Array.prototype.forEach() is that the former uses the @@iterator property to loop over the object, while the latter iterates the properties from 0 to length, and executes the callback only if the object has that property. It uses a [[HasProperty]] internal method, which in this case returns false for every array element.

The solution is to add also the has() handler, which will intercept the [[HasProperty]] calls.

Working code:

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./ajax/libs/babel-polyfill/6.16.0/polyfill.min.js"></script>

There is an additional option that is relatively simple. Use Array.from() to generate an array that you can iterate over.

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

The full code:

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));

if targetObject is of form [{a:1, b:2}, {a: 3, b:4}, {a: 5, b: 6}] to add sudoKey to each object we can below proxy

let proxy_array = new Proxy(targetObjectArray, {
        get: function (target, key) {
            if (!isNaN(parseInt(key))) {
                return { sudoKey: pute(), ...Reflect.get(target, key)}
            }
            return Reflect.get(target, key);
        }
    });

Example

let x = [{a: 1, b:2}, {a:3, b:4}, {a:5, b: 6}]

let y = new Proxy(x, {
        get: function (target, key) {
            if (!isNaN(parseInt(key))) {
                return { c: -1, ...Reflect.get(target, key)}
            }
            return Reflect.get(target, key);
        }
    });

y.forEach(e => console.log(e))
Output:
{c: -1, a: 1, b: 2}
{c: -1, a: 3, b: 4}
{c: -1, a: 5, b: 6}

here !isNaN(parseInt(key)) is to to check whether prop is ArrayIndex (0, 1, 2 ... x.length - 1) or Array.Prototype keys which include forEach as a property

MDN for Array.prototype which attach forEach the proxy Array

本文标签: javascriptArrayprototypeforEach() not working when called on a proxy with a get handlerStack Overflow