javascript 和记忆吸气剂

javascript and memoized getters

This article describe getters。它有一个部分“ 智能/自我覆盖/懒惰的吸气剂” 我不清楚,默认情况下是 getter 'memoized' 还是我应该自己实现此功能

例如

class Foo() {
  get boo() {
    this._boo = this._boo || new Boo(); 
    return this._boo;  
  }
}

或者我可以只写:

class Foo() {
  get boo() {
    return new Boo();  
  }
}

得到相同的结果?

不,JavaScript 中没有对 memoized getter 的语言级支持。在您的第二个示例中,每次访问 boo 时都会创建一个新对象。

考虑这段代码:

class Person {
    static get SHORT() { return 0; }//rvalue
}

对比

class Person {}
Person.SHORT = 0;//lvalue

虽然两者return结果相同,但后者实际上更快(因为它避免了函数调用开销);尽管 js 引擎可以进行优化,使一个无效。

那篇文章最有趣的部分是 Smart / self-overwriting / lazy getters,它提供了这种技术:

<del>
class Foo {
  get boo() {
    delete this.boo;
    return this.boo = new Boo();
  }
}
</del>

有了这个,您的 Foo 对象就不会经历创建它们的 boo 属性的麻烦,直到您提出要求。然后它被创建一次并进一步请求它 return 同一个对象。如果 new Boo() 在某种程度上需要大量资源来创建并且通常不需要,那么这是有道理的。

理论上,您可以扩展它以允许您删除当前版本并在下次访问时重新创建它。但那是更多的代码,而且可能是一个相当罕见的需求。

更新

A 正确地指出上述技术虽然适用于普通对象,但不适用于 类。

这是一个有效的变体:

class Boo {
  static counter = 0
  constructor () {
    this.x = ++Boo.counter
    console .log (`creating Boo(${this.x})`)
  }
}

class Foo {
  get boo () {
    Object .defineProperty (
      this, 
      "boo", 
      { value: new Boo(), writable: false}
    )
    return this .boo;
  }
}

const f = new Foo()

console .log (f.boo) 
console .log (f.boo) // no 'creating Boo' log, Boo constructor only created once

您可以随意添加备忘,例如

未记忆,

class NonMemoized {
  constructor(prefix) {
    this.prefix = prefix;
  }

  get myFunc() {
    return this.prefix + Math.random().toString();
  }
}

let nonMemoized = new NonMemoized('new number each time ');
console.log(nonMemoized.myFunc);
console.log(nonMemoized.myFunc);

记忆化,当你想创建一个对象一次并且总是 return 同一个对象时很好(但不想在构造函数中创建,因为可能没有必要一直或其他原因)

class MemoizedManually {
  constructor(prefix) {
    this.prefix = prefix;
  }

  get myFunc() {
    return this._myFunc_ = this._myFunc_ || this.prefix + Math.random().toString();
  }
}

let memoizedManually = new MemoizedManually('same number ');
console.log(memoizedManually.myFunc);
console.log(memoizedManually.myFunc);

最后,如果你有一堆函数你想记住但不想在每个函数中重复 this.x = this.x || something computation(你真的不应该重复,因为它不是真正的工作myFunc 自我记忆:

class Memoized {
  constructor(prefix) {
    this.prefix = prefix;
  }

  get myFunc() {
    return this.prefix + Math.random().toString();
  }
}

const memoizeGetter = (clazz, functionName) => {
  let func = Object.getOwnPropertyDescriptor(clazz.prototype, functionName);
  let cacheKey = `_${functionName}-cache_`;
  Object.defineProperty(clazz.prototype, functionName, {
    get: function () {
      return this[cacheKey] = this[cacheKey] || func.get.call(this);
    }
  });
};

memoizeGetter(Memoized, 'myFunc');

let memoized = new Memoized('also same number ');
console.log(memoized.myFunc);
console.log(memoized.myFunc);

getter 的好处是它们不接受参数,因此您不必担心 ...args,但需要担心绑定 this