I am attempting to create a Function extension to debounce any function (if the function is called multiple times in rapid succession, only execute it one time, and optimally return a cached value).
I intend to use it inside a UI framework, but I want it to be portable. The code I have so far is as follows:
Function.prototype.debounce = function()
{
var originalFunction = this; //this should be 'clickButton' in the example implementation
var originalArguments = arguments;
function debouncedFunction()
{
var originalContext = this;
return originalFunction.apply(originalContext,originalArguments)
}
if(this.__DEBOUNCEDVALUE === undefined)
{
this.__DEBOUNCEDVALUE = debouncedFunction();
if(this.__DEBOUNCEDVALUE === undefined)
this.__DEBOUNCEDVALUE = null;
setTimeout(function(){originalFunction.__DEBOUNCEDVALUE = undefined},1000);
}
return this;
}
Next, I defined a generic function, “clickButton”, which looks like this:
function clickButton()
{
document.getElementById('log').innerHTML += "<br/>Clicked "+arguments[1];
return "some value";
}
When I call clickButton.debounce(3,4), it works. It only logs to the document one per second, but returns every time it’s called.
However, when I call it through a listener (buttons[i].addEventListener('click',clickButton.debounce)), the originalFunction variable is set to the button, and not to the function. Not a big surprise. How do I get a reference to clickButton from within debounce in this example?
Edit:
I have tried switching to using defineProperty, which allows me to save the context and function at access time. Unfortunately, this does not work in all cases (putting it into a tap handler in Sencha Touch causes a function to be called within the Window scope). This is closer, but still unacceptable.
Object.defineProperty(Function.prototype,'debounce',{get: function()
{
var originalFunction = this;
var execute = function()
{
if(originalFunction.__DEBOUNCEDVALUE === undefined)
{
originalFunction.__DEBOUNCEDVALUE = originalFunction.apply(this,arguments);
if(originalFunction.__DEBOUNCEDVALUE === undefined)
originalFunction.__DEBOUNCEDVALUE = null;
setTimeout(function()
{
originalFunction.__DEBOUNCEDVALUE = undefined;
console.log("Reset");
},5000);
}
else
console.log("Return cached value");
return originalFunction.__DEBOUNCEDVALUE;
}
return execute;
}});
This can only be done via some method of binding the function object to the
debounce.One way is using
.bindAnother way is to pass an anonymous function that closes over the
clickButton.But aside from these sorts of techniques,
debouncewill have no memory of the object from which it was referenced when passed toaddEventListener.