无法将防抖ES5转换为ES6

时间:2019-05-06 23:10:28

标签: javascript ecmascript-6 ecmascript-5 debounce

我在线上找到了很棒的debounce()功能代码,但是我很难将其从ES5转换为ES6。

问题如下:当我使用ES5实现时,一切运行正常。调整窗口大小,立即触发console.log() ,随后的调整大小将被忽略,直到指定的500ms之后。

但是,在ES6实施中,第一个调用立即生效...但是每次之后,即使冷却下来,也会 延迟500ms!

如果有人知道我在做什么错,我将非常感谢您的帮助。

示例:

function debounceES5(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 debounceES6 = (callback, wait, immediate=false) => {
  let timeout = null
  return (...args) => {
    const callNow = immediate && !timeout
    const next = () => callback(...args)
    clearTimeout(timeout)
    timeout = setTimeout(next, wait)
    if (callNow) { next() }
  }
}

document.querySelector('.es5').addEventListener('click', debounceES5(() => {console.log('clicked')}, 1000, true))

document.querySelector('.es6').addEventListener('click', debounceES6(() => {console.log('clicked')}, 1000, true))
Click both of these buttons fast and see how they react differently

<br />

<button class="es5">ES5</button>
<button class="es6">ES6</button>

3 个答案:

答案 0 :(得分:2)

这是您用ES6编写的ES5函数-无需跳过任何详细信息(context除外)

const debounce = (func, wait, immediate=false) => {
    let timeout;
    return (...args) => {
        const later = () => {
            timeout = null; // added this to set same behaviour as ES5
            if (!immediate) func(...args); // this is called conditionally, just like in the ES5 version
        };
        const callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func(...args);
    };
};

请注意,later函数的功能与ES5版本完全相同

现在,它的行为(几乎)与ES5版本相同...整个context = this东西似乎在ES5版本中似乎完全怪异在示例用法中是有意义的

  

但是,根据注释,由于代码用于事件处理程序,this非常重要,因此它是事件目标,因此,您实际上无法返回arrow function

更好的代码是

const debounce = (func, wait, immediate=false) => {
    let timeout;
    return function (...args) {
        const later = () => {
            timeout = null; // added this to set same behaviour as ES5
            if (!immediate) func.apply(this, args); // this is called conditionally, just like in the ES5 version
        };
        const callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(this, args);
    };
};

答案 1 :(得分:1)

您不能在debounce中使用箭头功能(嗯,您需要知道可以在哪里和不能在哪里)

创建箭头功能 bind this。这意味着数组中的this永远也不会改变。

例如

'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");

regularFunc情况下this的通知未定义,稍后我们可以将其重新定义为hello,但是在arrowFunc情况下,通知始终为[Object Window]

所以您的ES6防抖没有。这是应该可以使用的ES6版本

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() }
    }
  }

让我们测试一下

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");

您可以看到es6版本失败,因为this错误。如果您只使用过非成员函数,那么es6Debounce似乎可以正常工作,但是一旦您在类或事件处理程序上使用成员函数,您就会发现es6Debounce不起作用,{{1 }}设置不正确。

此处的代码尝试显示该错误。 thisES5Class是相同的。测试应该打印

ES6Class

代替打印

ES5: [object Object] hello
ES6: [object Object] world

作为另一个示例,让我们尝试一个事件处理程序

ES5: [object Object] hello
undefined undefined world
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;
}

将鼠标移到2个区域上。再次注意es6一个错误。

这是否很重要,我不知道,但是您明确发布的原始<div id="es5">es5</div> <div id="es6">es6</div>包含使该案例有效的代码。

答案 2 :(得分:1)

我的首选解决方案是选自Chris Boakes'博客文章Understanding how a JavaScript ES6 debounce function works的以下解决方案:

function debounce(callback, wait) {
    let timeout;
    return (...args) => {
        const context = this;
        clearTimeout(timeout);
        timeout = setTimeout(() => callback.apply(context, args), wait);
    };
}

尽管它不提供immediate arg。仍然可以在这里共享。