最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ecmascript 6 - javascript and memoized getters - Stack Overflow

programmeradmin2浏览0评论

This article describe getters. It has a section " Smart / self-overwriting / lazy getters" And it's unclear for me, are getters 'memoized' by default or should I implement this feature by myself

e.g.

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

or can I just write:

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

to have the same result?

This article describe getters. It has a section " Smart / self-overwriting / lazy getters" And it's unclear for me, are getters 'memoized' by default or should I implement this feature by myself

e.g.

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

or can I just write:

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

to have the same result?

Share Improve this question asked Oct 19, 2017 at 14:32 kharandziukkharandziuk 12.9k18 gold badges74 silver badges127 bronze badges 3
  • 1 Whenever you get a value, the getter is called. Thats it. Theres no meoization until you implement it – Jonas Wilms Commented Oct 19, 2017 at 14:35
  • They are not by default, you have to do it yourself. – Rafael Commented Oct 19, 2017 at 14:35
  • 1 What it's saying is that getters are lazy by default, but you need to handle memorization yourself (and gives an example of how to implement such). – Phylogenesis Commented Oct 19, 2017 at 14:36
Add a comment  | 

5 Answers 5

Reset to default 6

The most interesting bit of that article was Smart / self-overwriting / lazy getters, which offers this technique:


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

With this your Foo objects don't go through the hassle of creating their boo properties until you ask for it. Then it's created once and further requests for it simply return the same object. This makes sense if new Boo() is in someway resource-intensive to create and reasonably often is not needed.

Theoretically, you could extend this to allow you to delete the current version and recreate it on next access. But that's a lot more code, and is probably a fairly rare need.

Update

A comment from vrugtehagel correctly pointed out that the above technique, while fine for plain objects, does not work for classes.

Here's a variant which does work:

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

No, there is no language-level support for memoized getters in JavaScript. In your second example, a new object would be created every time boo was accessed.

You're free to add memoization, e.g.

Non memoized,

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

Memoized, nice for when u want to create an object once and always return the same object (but don't want to create in the constructor because maybe it's not necessary all the time or some other reason)

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

Lastly, if you have a bunch of functions you want to memoize but don't want to repeat that this.x = this.x || something computation in each function (which you really shoudln't repeat, as it's not really the job of myFunc to memoize itself:

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

Nice thing about getters is they don't take arguments, so you don't have to worry about ...args, but do need to worry about binding this

consider this code:

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

versus

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

Although both return the same result, the latter is actually faster (because it avoids the function call overhead); though the js engine can make optimizations that nullify one over the other.

You can do it all on one line:

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

This is easy to remember and isn't convoluted. Got to consider the maintenance factor, and try to keep things simple.

发布评论

评论列表(0)

  1. 暂无评论