Results 1 to 6 of 6

Thread: JavaScript Closures: A tool for creating re-usable scripts

  1. #1
    Join Date
    Dec 2004
    Location
    UK
    Posts
    2,358
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default JavaScript Closures: A tool for creating re-usable scripts

    If one looks at the ECMAScript code I've posted to this forum, one should notice a simple pattern, or at least variations of it:

    Code:
    var identifier = (function() {
      /* ... */
    })();
    Code as simple as this will prevent the creation of unnecessary global variables, but provide something akin to private member variables available in other object-oriented languages (don't forget that ECMAScript is OO, just in a rather unique way). This operates on the basis that the function expression (shown above) acts like a wrapper to the code placed inside of it. Any variable created using the var keyword will become a local variable, usable by any code contained within the expression.

    This functionality is provided by a relatively unknown feature of ECMAScript: the closure. When a function is called, it creates an execution context. This context holds various bits of state information for that function, primarily: the scope chain (used to resolve identifiers), the arguments passed to the function, and the local variables and functions defined within.

    When an inner function (a function defined within another function) is created, this state information is added to its scope chain. This allows the inner function to reference the variables, functions, and formal (named) arguments defined by the outer function. If the inner function can survive (it would normally be destroyed when the outer function returns), a closure is formed. Consider:

    Code:
    function outerA() {
      var localA;
      function innerA() {
      }
    }
    
    function outerB() {
      var localB;
      function innerB() {
      }
      return innerB;
    }
    The function, outerA, defines two local variables; localA which is undefined, and innerA1 which is a function. If outerA calls innerA, the inner function could access localA. However, once outerA returns, both of the local variables will be destroyed.

    The function, outerB, is very similar. However, it returns a reference to its inner function, innerB. If you store this return value, the garbage collector cannot remove the inner function. Moreover, as the inner function has the execution context of outerB in its scope chain, localB and its value cannot be removed, either. This allows innerB to continue to reference and alter, if desired, the value of localB.

    What is even more useful is the "uniqueness" factor. If outerB is called a second time, a totally new execution context is created as this slightly more complicated example shows:

    Code:
    var a, b;
    
    /* Returns a function reference which, when
     * called, can reference the argument, arg
     * of the outer function.
     */
    function outer(arg) {
      return function() {
        return arg;
      };
    }
    
    a = outer(1);
    b = outer(2);
    
    a(); // returns 1
    b(); // returns 2
    Notice that the functions referenced by a and b maintain the initial value passed to outer; the second call doesn't overwrite it.

    How exactly one uses this pattern depends precisely on what is desired. The snippet at the very beginning of this post would provide one-time-only execution of the code within the function expression. This can be useful for providing singleton objects or private static members[1]:

    Code:
    var singleton = (function() {
      /* Private methods/variables */
    
      return {
        /* property : value  pairs */
      };
    })();
    
    var MyObject = (function() {
      /* Private static methods/variables */
    
      /* MyObject constructor */
      function MyObject() {
      }
      return MyObject;
    })();
    Private instance members[2] are equally possible:

    Code:
    function MyObject() {
      var privateMember = 5;
    
      this.method1 = function() {return privateMember;};
    }
    MyObject.prototype.method2 = function() {return this.privateMember;};
    In this example, method1 is said to be priviledged as it, unlike method2 has access to privateMember. Note that the return value of method2 would be undefined as there is no public member named privateMember.

    There are countless possible applications of closures, including the ability to reconfigure objects or functions at run-time based on the host environment (extremely useful for Web scripting). A detailed discussion can be found in the comp.lang.javascript FAQ notes and topics similar to this post at Douglas Crockford's website.

    Mike


    Credits go to Richard Cornford, Douglas Crockford and Lasse Reichstein Nielsen for introducing this concept, and related material, to me in the first place - I'm merely passing it along. Thanks also go to Yann-Erwan Perio, and the above, for their help and inspiration since I first began participating in comp.lang.javascript.

    [1] Public static members can be created by adding members directly to a constructor function:

    Code:
    function MyObject() {
    }
    MyObject.staticMethod = function() {
    };
    [2] Public instance members should be common knowledge. However there are two ways of creating them.

    1. Using the prototype object:
      Code:
      function MyObject() {
      }
      MyObject.prototype.method = function() {
        /* ... */
      };
      var obj = new MyObject();
      obj.method();
    2. Within the constructor:
      Code:
      function MyObject(param) {
        this.method = function() {
          /* ... */
        };
      }
      var obj = new MyObject();
      obj.method();
    Although both snippets would have the same external functionality, they both serve two very different purposes. The latter example should be used if (and only if)
    1. A data member needs to be initialised at object construction-time based on something, most likely an argument to the constructor.
    2. A method needs to be priviledged. That is it needs to access private data.

  2. #2
    Join Date
    Dec 2004
    Location
    UK
    Posts
    2,358
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default

    Well, it seems that several people have now read this thread. Someone even gave it an excellent rating. But no questions or comments?

    Oh well.

    Mike

  3. #3
    Join Date
    Mar 2005
    Location
    SE PA USA
    Posts
    29,126
    Thanks
    44
    Thanked 3,228 Times in 3,189 Posts
    Blog Entries
    12

    Default OK, I've got some questions on this

    When you use 'function()' in the code sections above, do you mean that as a literal or as a stand in for the name of some function?

    I'd be happy, at this point, if I could just write something like this:

    Code:
    test_preload = function(imgobj){
    xtd=imgobj;
    is_loaded=true;
    if (xtd.complete==false)
    is_loaded=false;
    if (is_loaded==false)
    setTimeout("test_preload(xtd)",2000);
    }
    and have that be different than:
    Code:
    function test_preload(imgobj){
    xtd=imgobj;
    is_loaded=true;
    if (xtd.complete==false)
    is_loaded=false;
    if (is_loaded==false)
    setTimeout("test_preload(xtd)",2000);
    }
    In what way would it be different, if in fact it would? Especially, could the first method appear as part of another function and cause local looping in that other function until is_loaded is true? I've tried this with a do/while but the test, if performed, has no bearing on the flow of the rest of the script. I've written a script that includes the above test and uses it but, a more integrated approach was required (the test had to be part of the overall script flow).

  4. #4
    Join Date
    Dec 2004
    Location
    UK
    Posts
    2,358
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default

    Quote Originally Posted by jscheuer1
    When you use 'function()' in the code sections above, do you mean that as a literal or as a stand in for the name of some function?
    There are two ways to create a function object: function declarations and function expressions. I explained the difference fairly recently on Usenet, so if I may, I'd prefer to just direct you to that post. I also posted a few corrections and clarifications which Google decided not to thread.

    In what way would it be different, if in fact it would?
    Only the point at which the resulting function object would be available.

    Especially, could the first method appear as part of another function
    It depends what you mean by "appear as part". You certainly could use an inner function to alter a local variable in an outer function:

    Code:
    /* This snippet is not executable as-is! */
    function myFunction() {
      var myBool = false;
    
      function inner(){
        if(condition) {myBool = true;}
      }
    
      while(!myBool) {
        inner();
      }
    }
    However, your example is rather awkward because of how setTimeout works. In modern user agents, the function can take either a function reference, or a string. With the former, any function reference will do and it can take advantage of local variables in its scope chain:

    Code:
    function myFunction() {
      var i = 0;
    
      function inner() {
        alert(i++);
      }
    
      setTimeout(inner, 2000);
    }
    With the string version, the code is evaluated in global scope, so replacing the inner argument with the string, 'inner();', would cause an error because the inner function wouldn't exist. Unfortunately, some older user agents only accept this form, so it makes relying soley on the more flexible function version somewhat unwise. That said, there is a way to use both. An article in the comp.lang.javascript FAQ notes explains this.

    I've tried this with a do/while but the test
    Polling isn't generally a good idea for client-side scripts. Whilst a script is executing, a user agent will typically do nothing else making it appear that the UI is frozen. A better approach would be to use a callback that executes whatever you want once the image has finished loading. It could either be called from a load event listener on the image, or by periodically checking the complete property. However, neither approach is standardised - be aware of that. Particularly, before using the complete property, check that it's actually supported using a typeof operator test:

    Code:
    if(('boolean' == typeof img.complete) && !img.complete) {
      /* complete property supported, but not set to true */
    }
    Mike

  5. #5
    Join Date
    Mar 2005
    Location
    SE PA USA
    Posts
    29,126
    Thanks
    44
    Thanked 3,228 Times in 3,189 Posts
    Blog Entries
    12

    Default

    This raises a number of other questions, two I'll ask here, and begs a point. First question:

    Will this syntax work for all older browsers as a test to get them past a method they don't understand without giving an error?

    Code:
    if ('boolean' == typeof object.method){
    code here that uses object.method
    }
    code here that continues on only using universally supported methods
    and alternatively:
    Code:
    if ('boolean' == typeof object.method){
    code here that uses object.method
    }
    else{
    code here that uses best universally supported alternative
    }
    continue on with universally supported code
    Second question: I want something that works like the DOS batch file 'call', stopping processing while another process is carried out and then resuming processing. I've been assuming there is no such thing in JavaScript because I've never seen it and because the admin here told me that there are ways to emulate it but that they vary depending upon the situation. What are your thoughts on this?

    Now to beg a point underline added:
    Polling isn't generally a good idea for client-side scripts. Whilst a script is executing, a user agent will typically do nothing else making it appear that the UI is frozen.
    What if, at the point polling is undertaken, the page is drawn, all images areas have an image displayed in them, all visible links are available. Wouldn't this be a good time to poll?

  6. #6
    Join Date
    Dec 2004
    Location
    UK
    Posts
    2,358
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default

    Quote Originally Posted by jscheuer1
    This raises a number of other questions,
    Fire away...

    Will this syntax work for all older browsers as a test to get them past a method they don't understand without giving an error?

    Code:
    if ('boolean' == typeof object.method){
    code here that uses object.method
    }
    code here that continues on only using universally supported methods
    Not with a boolean typeof test, no as methods are of type function but the typeof operator is well supported. With standardised methods, you can limit yourself to a type-conversion test:

    Code:
    if(object.method) {
      /* Use object.method */
    }
    A function reference will always type-convert to boolean true, so that should be all that's necessary. It's also preferable as host methods may not always be reported as type function. That is,

    Code:
    if('function' == typeof document.getElementById) {
      /* ... */
    }
    may not always be true. IE has this odd habit of reporting host methods as objects - &deity; only knows why. User-defined functions and those defined by ECMA-262 (ECMAScript), such as the methods of Date objects, should always be functions.

    When dealing with properties, you should always perform a typeof test to ensure they're supported before relying on them. Primitive types such as strings and numbers can all type-convert to false given the right value, so

    Code:
    if(object.property) {
      /* ... */
    }
    may not be sufficient. Consider what happens with your polling loop if the host doesn't support the complete property. The value will always be undefined, so the loop will never exit. There's a problem with that sort of dependent loop anyway: what if the image fails to load? A network error or a browser configured to block images would also cause the script to loop forever. If you must poll, always provide a timeout mechanism unless you can guarantee that the loop will exit eventually.

    Second question: I want something that works like the DOS batch file 'call', stopping processing while another process is carried out and then resuming processing.
    What are you trying to "call"?

    Now to beg a point underline added:

    Quote Originally Posted by mwinter
    Polling isn't generally a good idea for client-side scripts. Whilst a script is executing, a user agent will typically do nothing else making it appear that the UI is frozen.
    What if, at the point polling is undertaken, the page is drawn, all images areas have an image displayed in them, all visible links are available. Wouldn't this be a good time to poll?
    I'd still say no. Whilst the UI would be drawn, it would still be unresponsive. I said "generally" because polling for a truly short duration (mere milliseconds) shouldn't be noticable.

    Polling doesn't occur in well-designed applications anymore (except at low-level) unless the priority of that thread can be lowered, which keeps the interface interactive. However, there is no multi-threading in client-side scripting so you should take the modern approach and use event-driven code wherever possible.

    Mike

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •