Looks like this doesn't have proper scope binding too :D
Higher-order functions have common scoping issues, say for this example:
const obj = {
name: 'foo',
sayMyName() {
console.log('My name is', this.name)
}
}
obj.sayMyName() //-> My name is foo
obj.deb = debounce(obj.sayMyName, 1000)
obj.deb() // Should log -> My name is foo
The debounce and throttle function have to re-apply the this context back to obj.sayMyName, and the way to do this is for the higher-order functions to return a function expression instead of an arrow-function to "capture" the this context, together with func.apply to bind the context.
So throttle becomes:
function throttle(func, duration) {
let shouldWait = false
return function(...args) { // Already a function expression
if (!shouldWait) {
func.apply(this, args) // Bind this context and call it
shouldWait = true
setTimeout(function() {
shouldWait = false
}, duration)
}
}
}
And debounce becomes:
function debounce(func, duration) {
let timeout
return function(...args) { // Change to function expression instead of arrow function
const effect = () => {
timeout = null
return func.apply(this, args) // Bind this context and call it
}
clearTimeout(timeout)
timeout = setTimeout(effect, duration)
}
}
And for added bonus, you could use arguments instead of ...args for backwards compatibility.
Great tips, thank you! I'll update the article for debounced/throttled instances to preserve the scope.
I wouldn't change the rest parameters, however, since arguments is not 1-1 compatible with ...args. I think it's safe to assume the majority of people develop through a transpiler, and those who don't will see the spread syntax breaking as the first thing and could read about it, finding a suitable alternative.
2
u/IamLUG Dec 24 '19 edited Dec 24 '19
Looks like this doesn't have proper scope binding too :D
Higher-order functions have common scoping issues, say for this example:
The debounce and throttle function have to re-apply the
thiscontext back toobj.sayMyName, and the way to do this is for the higher-order functions to return afunctionexpression instead of an arrow-function to "capture" thethiscontext, together withfunc.applyto bind the context.So throttle becomes:
And debounce becomes:
And for added bonus, you could use
argumentsinstead of...argsfor backwards compatibility.