Skip to content

30 seconds of code

How can I implement a singleton in JavaScript?

  • The Proxy object is used to define so-called traps(捕获器)
  • handler.construct(), the trap for the new operator
// Examples
class MyClass {}
const MySingletonClass = singletonify(MyClass);

const myObj = new MySingletonClass();
const myObj2 = new MySingletonClass();
console.log(myObj === myObj2);
const singletonify = (className) => {
  return new Proxy(className.prototype.constructor, {
    instance: null,
    construct(target, args) {
      if (!this.instance) {
        this.instance = new target(...args);
      }
      return this.instance;
    }
  });
}

Where and how can I use memoization in JavaScript?

  • use to speed up your code
  • uses a cache to store results, so that subsequent calls of time-consuming functions do not perform the same work another time.
  • JavaScript's Proxy object provides an interesting alternative via the use of the handler.apply() trap
// Examples
const fibonacci = n => (n <= 1 ? 1 : fibonacci(n - 1) + fibonacci(n - 2));
const memoizedFibonacci = memoize(fibonacci);

for (let i = 0; i < 100; i ++)
  fibonacci(30);                      // ~5000ms
for (let i = 0; i < 100; i ++)
  memoizedFibonacci(30);   
const memoize = (fn) => {
  return new Proxy(fn, {
    cache: new Map(),
    apply(target, thisArg, argumentsList) {
      let cacheKey = argsList.toString();
      if (!this.cache.has(cacheKey)) {
        this.cache.set(cacheKey, target.apply(thisArg, argumentsList))
      }
      return this.cache.get(cacheKey);
    }
  });
}

runAsync

  • Runs a function in a separate thread by using a Web Worker, allowing long running functions to not block the UI.
const runAsync = (fn) => {
  const url = URL.createObjectURL(new Blob([`postMessage((${fn})());`]), {
    type: 'application/javascript; charset=utf-8'
  })
  const worker = new Worker(url);

  return new Promise((r, j) => {
    worker.onmessage = ({ data }) => {
      r(data);
      worker.terminate();
    };
  });
}

// Examples
const longRunningFunction = () => {
  let result = 0;
  for (let i = 0; i < 1000; i++)
    for (let j = 0; j < 700; j++)
      for (let k = 0; k < 300; k++) result = result + i + j + k;

  return result;
};

runAsync(longRunningFunction).then(console.log); // 209685000000
runAsync(() => 10 ** 3).then(console.log); // 1000
let outsideVariable = 50;
runAsync(() => typeof outsideVariable).then(console.log); // 'undefined'

debouncePromise

  • Creates a debounced function that returns a promise, but delays invoking the provided function until at least ms milliseconds have elapsed since the last time it was invoked
// Examples
const fn = arg => new Promise(resolve => {
  setTimeout(resolve, 1000, ['resolved', arg]);
});
const debounced = debouncePromise(fn, 200);
debounced('foo').then(console.log);
debounced('bar').then(console.log); // 打印 ['resolved', 'bar'] 两次
const debouncePromise = (fn, ms = 0) => {
  let timeoutId;
  const pending = [];

  return (...args) => new Promise((r, j) => {
    clearTimeout(timeoutId);

    timeoutId = setTimeout(() => {
      const currentPending = [...pending];
      pending.length = 0;

      Promise.resolve(fn.apply(this, args)).then(
        value => {
          currentPending.forEach(({ resolve }) => resolve(value));
        },
        reason => {
          currentPending.forEach(({ reject }) => resolve(reason));
        }
      );
    }, ms)

    pending.push({ resolve: r, reject: j });
  });
}

pipeAsyncFunctions

const sum = pipeAsyncFunctions(
  x => x + 1,
  x => new Promise(resolve => setTimeout(() => resolve(x + 2), 1000)),
  x => x + 3,
  async x => (await x) + 4
);

sum(5).then(console.log)
const pipeAsyncFunctions = (...fns) => (arg) => {
  return fns.reduce((acc, fn)=> acc.then(fn), Promise.resolve(arg))
}