You can't use arrow functions in debounce
(well, you need to know where you can and where you can't)
Arrow function bind this
when they are created. That means the this
in an array will never and can never change.
For example
'use strict';
function safeToString(v) {
return v === undefined
? 'undefined'
: v.toString();
}
function regularFunc() {
console.log('regular:', safeToString(this));
}
const arrowFunc = () => {
console.log('arrow:', safeToString(this));
};
regularFunc();
arrowFunc();
regularFunc.call("hello");
arrowFunc.call("world");
Notice in the regularFunc
case this
is undefined and later we can redefine it to hello
but in the arrowFunc
case it's always [Object Window]
So no to your ES6 debounce. Here's The supposedly working ES6 version
const debounce = (callback, wait, immediate) => {
let timeout;
return (...args) => {
const callNow = immediate && !timeout
const next = () => callback(...args)
clearTimeout(timeout)
timeout = setTimeout(next, wait)
if (callNow) { next() }
}
}
Let's test it
function es5Debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this,
args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
const es6Debounce = (callback, wait, immediate) => {
let timeout;
return (...args) => {
const callNow = immediate && !timeout
const next = () => callback(...args)
clearTimeout(timeout)
timeout = setTimeout(next, wait)
if (callNow) {
next()
}
}
}
function safeToString(v) {
return v === undefined
? 'undefined'
: v.toString();
}
class Test {
constructor(name) {
this.name = name;
}
log(...args) {
console.log(
this ? this.name : 'undefined',
safeToString(this),
...args);
}
}
class ES5Test extends Test {
constructor() {
super('ES5:');
}
}
ES5Test.prototype.log = es5Debounce(ES5Test.prototype.log, 1);
class ES6Test extends Test {
constructor() {
super('ES6:');
}
}
ES6Test.prototype.log = es6Debounce(ES6Test.prototype.log, 1);
const es5Test = new ES5Test();
const es6Test = new ES6Test();
es5Test.log("hello");
es6Test.log("world");
As you can see the es6 version fails because this
is wrong. If you only ever use non-member functions then es6Debounce
will look like it's working but as soon as you use member functions on a class or event handlers you'll see es6Debounce
does not work, this
is not set correctly.
The code here tries to show the error. ES5Class
and ES6Class
are identical. The test should print
ES5: [object Object] hello
ES6: [object Object] world
instead it prints
ES5: [object Object] hello
undefined undefined world
As another example let's try an event handler
function es5Debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this,
args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
const es6Debounce = (callback, wait, immediate) => {
let timeout;
return (...args) => {
const callNow = immediate && !timeout
const next = () => callback(...args)
clearTimeout(timeout)
timeout = setTimeout(next, wait)
if (callNow) {
next()
}
}
}
function mousemove(e) {
console.log(this.id, e.pageX, e.pageY);
}
document.querySelector('#es5')
.addEventListener('mousemove', es5Debounce(mousemove));
document.querySelector('#es6')
.addEventListener('mousemove', es6Debounce(mousemove));
#es5, #es6 {
margin: 1em;
width: 10em;
height: 2em;
display: inline-block;
}
#es5 {
background: orange;
}
#es6 {
background: yellow;
}
<div id="es5">es5</div>
<div id="es6">es6</div>
Move the mouse over the 2 areas. Notice again the es6 one is wrong.
Whether or not that's important do you I have no idea but the original debounce
you posted explicitly has code to make that case work.