Array.prototype.forEach () does not work when calling proxies with get handler

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

This is a massive object because it has numeric properties and a length property that determines the number of elements. I can iterate with a for...of loop:

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

However, the forEach() method does not work.

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

This code does not write anything. The callback function is never called. Why is this not 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> 
+6
source share
1 answer

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

The solution is to add a has() handler that intercepts calls to [[HasProperty]] .

Work 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> 
+6
source

Source: https://habr.com/ru/post/1011996/


All Articles