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.com/ajax/libs/babel-polyfill/6.16.0/polyfill.min.js"></script>
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.com/ajax/libs/babel-polyfill/6.16.0/polyfill.min.js"></script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With