document smart/memoized/self-overwriting getter

RESOLVED FIXED

Status

Developer Documentation
JavaScript
RESOLVED FIXED
3 years ago
2 years ago

People

(Reporter: Steffen Wilberg, Unassigned)

Tracking

Details

(URL)

(Reporter)

Description

3 years ago
Document smart/memoized/self-overwriting getters.

Code example:

get notifier() {
  delete this.notifier;
  return this.notifier = document.getElementById("bookmarked-notification-anchor");
},

http://mxr.mozilla.org/mozilla-central/source/browser/base/content/browser-places.js#1208
I guess an additional example on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get is fine.

Can you explain what memoized/self-overwriting getters are? (and ideally add an example to this wiki page)
Steffen, is this something that you are planning to do?
Flags: needinfo?(steffen.wilberg)
(Reporter)

Comment 4

3 years ago
I'd rather not. I have an idea, but I don't think I can explain this correctly.
Flags: needinfo?(steffen.wilberg)
CC'ing some (random) people who might be able to explain what memoized/self-overwriting getters are.
I think that kind of thing is referred as lazy getter in firefox codebase, implemented as XPCOMUtils.defineLazyGetter.
https://dxr.mozilla.org/mozilla-central/source/js/xpconnect/loader/XPCOMUtils.jsm#187
Here's my understanding, please correct me if I'm misunderstanding the topic or something ;)

It's a technique to lazify (delay?) the calculation of the property value and cache it for later access.  It's useful for following cases:

  1. calculation of the property value is expensive
  takes much RAM or CPU time, spawns worker thread, retrieves remote file, etc

  2. that value isn't needed just now
  it will be used later, or in some case it's not used at all (if it's not used, we don't want to calculate!)

  3. if it's used, it will be accessed several time, and no need to re-calculate
  that value will never be changed, or shouldn't re-calculate

To satisfy above, we can use getter function, and re-define it as normal property inside the getter function.  So the calculation is executed only once on initial access, and same value is returned for later accesses.

  var obj = {
    get prop() {
      var propValue = some_expensive_function();
      Object.defineProperty(this, "prop", { value: propValue });
      return propValue;
    }
  };

  // some_expensive_function is not yet called here.

  // ...

  var x = obj.prop; // some_expensive_function is called here,
                    // and the property is re-defined.

  console.log(x == obj.prop); // some_expensive_function is *NOT* called here,
                              // cached value is returned for this time,
                              // and it prints true.


Here's another example.
  https://dxr.mozilla.org/mozilla-central/source/toolkit/components/promiseworker/PromiseWorker.jsm#171
_worker property returns the Worker instance. the getter spawns Worker on initial access, and re-define that property as a normal property with the instance as its value.  So that the Worker won't be instantiate unless/until it's really needed.

Comment 9

3 years ago
(In reply to Steffen Wilberg from comment #0)
> Document smart/memoized/self-overwriting getters.
> 
> Code example:
> 
> get notifier() {
>   delete this.notifier;
>   return this.notifier =
> document.getElementById("bookmarked-notification-anchor");
> },
> 
> http://mxr.mozilla.org/mozilla-central/source/browser/base/content/browser-
> places.js#1208

What's happening here is:
* the object has a getter as own property
* on getting the property, the property is removed from the object (delete) 
* and re-added, but implicitely as a data property this time (this.notifier =)
* the value is returned.

This is smart, but requires a good low-level knowledge of how JS objects work and makes lots of non-trivial assumptions (1) it's an own property, not an inherited one, 2) the property is configurable at all time, 3) there is no setter with the same name in the prototype chain). 
Rather than documenting the pattern, I'd recommend using defineLazyGetter or equivalent which is more explicit and hides all the low-level JS plumbing.

Comment 10

3 years ago
(In reply to Tooru Fujisawa [:arai] from comment #8)
> It's a technique to lazify (delay?) the calculation of the property value
> and cache it for later access.
Yes, exactly. In this case, it might also be a way to delay the document.getElementById call so it happens when the DOM is ready (to prevent returning null), but I'm not familiar enough with the code to tell for sure.
I have added some of the wisdom from the bug comments here to
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get#Smart_self-overwriting_lazy_getters

This is also mentioned on the XPCOMUtils.jsm page:
https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/XPCOMUtils.jsm#defineLazyGetter%28%29

Resolving, but feel free to edit and enhance these two wiki pages further.
Status: NEW → RESOLVED
Last Resolved: 2 years ago
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.