Не могу конвертировать Debounce ES5 в ES6 - PullRequest
2 голосов
/ 07 мая 2019

В сети я нашел отличный debounce() код функции, но мне трудно преобразовать его из ES5 в ES6.

Проблема заключается в следующем: когда я использую реализацию ES5, все работает отлично,Изменение размера окна, console.log() - это немедленно , и последующие изменения игнорируются до тех пор, пока я не укажу 500 мс.

Однако в реализации ES6 первый вызов работает немедленно ..... но каждый раз после этого также задерживается на 500 мс, даже после охлаждения!

Если есть кто-то, кто знает, что я делаю неправильно, я был бы очень признателен за помощь.

Примеры:

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>

Ответы [ 2 ]

2 голосов
/ 07 мая 2019

Вот ваша функция ES5, написанная на ES6 - без пропуска ЛЮБЫХ деталей (кроме 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

Код BEtter будет

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 голос
/ 07 мая 2019

Вы не можете использовать функции стрелок в 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 не работает, this не установлен правильно ,

Код здесь пытается показать ошибку. ES5Class и 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;
}
<div id="es5">es5</div>
<div id="es6">es6</div>

Наведите указатель мыши на 2 области. Еще раз обратите внимание на то, что es6 ошибочен.

Независимо от того, важно ли это для вас, я понятия не имею, но исходный debounce, который вы опубликовали явно, содержит код, чтобы заставить это дело работать.

...