0

I'm working on dividing a working page script between the page/content script and an extension background script; and because a communication port doesn't have a return message that functions like a promise, such that the content script code can wait for a promise in the background script to complete before processing the next desired step, I've been using the communication port to invoke a function in the content script by adding functions to an object. That has worked well and permits the building of a promise.then chain in the content script that waits for the background script promise to resolve/reject. Things are a little different, however, when doing something similar for moving the indexedDB databases from the page to the extension.

I want to do something like this in the content script.

var f = new Object();            
f.db_event_1 = function()
  {
    // Prepare data.
    // Declare functions for background script to invoke in content script
    // for oncomplete and onerror/onabort events.

    f.db_event_1_.complete = function() { };
    f.db_event_1_.error = function() { };

    // Communicate with background script, passing data, database name, object store 
    // names, etc. and invoke database transaction in the background script.

    // Wait (not an asyc function wait) for background script to invoke either of the 
    // above two functions based upon whether the transaction succeeds or fails.

  };

I don't fully grasp function variable scope and performed some simple tests to determine how variables declared in a function are "remembered" by functions declared within that function when later invoked. For example, I don't understand why f.func_outer.inner "remembers" that x was 3 and y was 5 when it was declared.

Can it be relied upon that f.func_outer.inner will always "remember" variables in this manner?

If the variables are large in size, should they be set to null at the close of func_outer.inner in order to break all variable references to avoid potential memory leaks?

Thank you.

var f = new Object();            
f.func_outer = function( x )
  {
    console.log( 'func_outer x = ' + x + ', y = ' + y );
    f.func_outer.inner = function()
      {
        console.log( 'func_outer.inner x = ' + x + ', y = ' + y );
      };
  };
f.func_outer( 3 );   // logs func_outer x = 3, y = 5.
f.func_outer.inner();  // logs func_outer.inner x = 3, y = 5.
Gary
  • 2,393
  • 12
  • 31
  • Does this answer your question? [How do JavaScript closures work?](https://stackoverflow.com/questions/111102/how-do-javascript-closures-work) –  Sep 15 '20 at 05:52
  • Thanks. Sort of. As I wrote below, I didn't realize that a closure is made when something isn't returned or executed from within the outer function. – Gary Sep 15 '20 at 05:59
  • Also, since the scope persists after the outer function runs, even without returning something, will setting the variables in that scope to null eliminate the chance of a memory leak? How is the memory that holds the values of the closure scope ever released by the garbage collector. – Gary Sep 15 '20 at 06:05
  • "Does this answer your question?" is just the standard phrasing that is added automatically when you mark a question as duplicate; it used to be different but SO wanted to be more welcoming, so now every newbie thinks the other person actually typed that :/ ;) I'm not an expert on closures, but the duplicate should be a good starting point to find existing answers. –  Sep 15 '20 at 06:10
  • I see. I didn't know that. It doesn't answer my question. My question has two parts, the latter being rather significant. After having the discussion below, it appears that the answer to my question is that having the inner function is not a good practice unless a closure is needed to capture values at a point in time, because it consumes RAM that the GC cannot release without further programmatic action making memory leaks possible. If I checked that radio button ten minutes ago and closed the question, I wouldn't have learned that through the comments below. Thanks. – Gary Sep 15 '20 at 06:45

1 Answers1

0

Yes, you can rely on it. It is called closures in js, more info in this topic can be found here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

  • Thanks. I understand the examples and concept but they all return something or execute the inner function from within the outer function. My example does neither and I didn't think it was making a closure. – Gary Sep 15 '20 at 05:58
  • @Garry in your last example you have closures also, I will try to explain how it works. `f.func_outer.inner` is undefined at first, then you call `f.func_outer` with an argument of `x = 3`, then it creates `f.func_outer.inner`. Now closure will kick in because inner function is in the scope of outer, which means it will have acces to variables from global scope + variables from outer function. Finally you call `f.func_outer.inner();` and now it has access to x that you passed to outer. If you call `f.func_outer(4);` for example, it will create new function inner and x will be 4 now. – Vadim Liakhovich Sep 15 '20 at 06:04
  • Thank you. I understand. If `x` is very large or contains a reference to a DOM node, do you know how the closure can be released by the garbage collector, that is, how can the program let the GC know that `f.func_outer.inner` won't be invoked again and that the memory holding `x` and `y` can be released? Also, is the memory from the previous invocations released when a new invocation is made? – Gary Sep 15 '20 at 06:09
  • @Gary When there are no references to it anymore. So as long as the function exists (in your case it is referred to by the outer function which belongs to the object) the variable will stay in RAM. Once the function is deleted (in your case the object is destroyed) everything captured by its closure will be freed – slebetman Sep 15 '20 at 06:13
  • @Garry very good question! Indeed you will need to set big variable to null when you do not need it anymore in order to be garbage collected, but also if you don't need outer function and the object that uses it will be dereferenced the whole chain will be also garbage collected. You can read more about memory management here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management – Vadim Liakhovich Sep 15 '20 at 06:13
  • 1
    @Gary You may be interested in my answer to this other question because it contains some nice ASCII art explaining how closures work: https://stackoverflow.com/questions/26061856/javascript-cant-access-private-properties/26063201#26063201 – slebetman Sep 15 '20 at 06:15
  • May I ask another follow up question please? If I need to use this function repeatedly as the user adds content to capture in the database, is there a way to re-use the same memory allocation over and over again instead of setting variables to null to be GC'd and creating new ones? I do that in other areas but it appears here that every invocation of the outer function will create every thing new again, meaning a new memory allocation for the closure on the scope mainatined for the inner function. – Gary Sep 15 '20 at 06:24
  • It seems an inner function is not such a good idea in terms of memory usage. I could make the inner functions independent outer functions. I don't really need a closure to capture any values at a point in time for this purpose. I was just keeping the same structure as is done in a databse transaction where the `oncomplete` and `onerror` handlers are definied within the same function. Perhaps, that is a bad idea when dividing the code like this in an extension. Thanks. – Gary Sep 15 '20 at 06:28
  • The closure document didn't answer my full question but these comments helped me to get a better view of how an inner function holds RAM through the closure and the risks of doing so. So, I'm selecting it as an answer because of that. Thanks for the help. – Gary Sep 15 '20 at 06:48
  • In reading the document again, the Performance Considerations section addresses the second part of my question. Although the concepts of scope, closures, object methods, are understandable, many questions arise. For example, when the inner functions are made methods of the object by appending to the prototype, and the methods act on properties added to the function object, those properties are not local variables "cleaned up" after the function completes but remain, not in a closure scope frame but as live property values. Much to be aware of concerning location and duration of variables. – Gary Sep 16 '20 at 01:26