What is the correct way to build Javascript proxies for arrays so that 'set' handlers do not get invoked multiple times for a single change to the array?
Here is what I mean:
I want to wrap a simple array in a Proxy object. I want a 'set' handler to run when I wish to push() a new value to this Proxy object.
The trouble is that proxy handlers like 'set' get called multiple times for one operation to an array. In some cases it seems fairly easy to deal with the problem but in other cases one call to modify an array wrapped in a Proxy object cases the set handler to be called at least one time for every element.
Suppose I created the simplest Proxy handler object and Proxy like this:
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);
This proxy is just intercepting 'set-like' calls to my proxied array and writing a message to console. Now, when I add a new element to the end of my proxyArray object:
proxyArray.push("three")
I'll get something like this:
Set handler is invoked on property: 3 with value: three
Set handler is invoked on property: length with value: 4
I see that the set handler got called twice: once for the creation of a new element in the array and once more for setting the new length property of the array.
Ok, this issue can be handled by checking for the property being manipulated. I've seen set properties done something like this:
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);
}
The same call to proxyArray.push("three") will perform the important things on all but the length property. This is because I'm checking if the length property is being set. This seems ok to me.
But, suppose I want to simply splice() something out of my array?:
proxyArray.splice(0,1);
That produces one 'set' invocation for every element in the array:
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
This is certainly not what I wanted. I wanted my set handler to run once on the 'splice()', not three times.
What is more, there's a very nasty side-effect of having 'set' methods triggered multiple times for the same splice() operation. Looking at the contents of the array by changing the 'set' handler to this:
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;
}
Will yield this:
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"]
So, Javascript is shifting each value down the array, one at a time, then popping off the last, duplicate element as the last step. Your set handler would have to be built to handle that sort of intermediate duplication.
That would seem to yield nasty and complicated 'set' handlers.
So, what is the proper way to build Javascript proxies wrapped around arrays so that 'set' handlers do not get invoked an unwanted multiple of times and the target object is reliable in the process?
sethandler being invoked multiple times? Specifically where "reliable in the process" is portion of requirement?sethandler if there is an issue with the handler being called for each index change? Is requirement to create a function which does not verify the.lengthof the array when an index is changed within the array?targetofProxy()call?