Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 6 of 6
  1. #1
    WA
    WA is offline
    Administrator
    Join Date
    Mar 2002
    Posts
    2,596
    Thanks
    2
    Thanked 19 Times in 18 Posts

    Is this a closure and hence should be avoided?

    Hi:
    Lets say I have a script that accepts a JS object literal as a parameter, such as:

    Code:
    myscript.init({
    id: "Bob",
    age: 32,
    height: "159cm"
    })
    My dilemma is how to store this literal once it enters my script- not store it and just pass it around as a parameter within myscript when it's needed, or save the literal inside a global variable immediately and access that instead? For example, myscript may look something like:

    Code:
    init:function(settings){
    "
    "
    setInterval(function(){myscript.repeat(setting)}, 1000)
    document.body.onclick=function(){
    alert(setting.id)
    }
    }
    or this instead:
    Code:
    init:function(settings){
    this.settingvar=settings
    "
    //myscript.repeat() looks for this.settingvar instead
    setInterval(function(){myscript.repeat()}, 1000)
    document.body.onclick=function(){
    alert(myscript.settingvar.id)
    }
    }
    From what I understand, in the first scenario, the setting parameter is being passed as a closure when setInterval() or body.onclick is attached, and hence is inefficient compared to the 2nd scenario right? In general for functions that accept object literals as parameters, is the best practice always to immediately store it inside a global variable instead of passing it around as a parameter within the script?

    Thanks for any insight.
    Last edited by WA; 05-01-2008 at 11:22 PM.
    - George
    - JavaScript Kit- JavaScript tutorials and 400+ scripts!
    - JavaScript Reference- JavaScript reference you can relate to.

  • #2
    Regular Coder
    Join Date
    Jun 2007
    Location
    USA
    Posts
    527
    Thanks
    26
    Thanked 74 Times in 72 Posts
    You are correct in that your first example creates a closure. Well . . . it would if you didn't misspell setting as settings (or is it settings vs. setting?)

    is the best practice always to immediately store it inside a global variable instead
    Globals are to be avoided! If you meant store it as a property of the object at hand, then perhaps. However, closures can do miraculous things . . . in a clear manner to boot.

    Without knowing too much about what your other code is, I would choose the closure variant in this case. It is cleaner.

    PS: If you don't do it already, get in the habit of ending each statement explicitly with a semicolon! (I also hope you define local variables with the keyword var.)

    Edit:
    Don't be afraid of closures for speed/space reasons, which shouldn't be problems anyway. The only reason to be cautious about them is with IE DOM not because of JS, but because IE is crap. In all, closures are too good to be true. I use them all the time!
    Last edited by Trinithis; 05-01-2008 at 11:50 PM.
    Trinithis

  • #3
    Master Coder
    Join Date
    Feb 2003
    Location
    UmeŚ, Sweden
    Posts
    5,575
    Thanks
    0
    Thanked 83 Times in 74 Posts
    Quote Originally Posted by WA View Post
    From what I understand, in the first scenario, the setting parameter is being passed as a closure when setInterval() or body.onclick is attached, and hence is inefficient compared to the 2nd scenario right? In general for functions that accept object literals as parameters, is the best practice always to immediately store it inside a global variable instead of passing it around as a parameter within the script?
    Actually, in both cases you are creating a closure over the init function's local scope. This means that both of them store a reference to the settings variable. Whether the variable is used within a nested function or not does not change whether the nested function closes over the variable or not. All outer local variables are always part of the closure.

    You can however set the variable to null in the outer function if you want to make sure your inner closure does not keep the object that variables points to alive.



    I try to code so that anything I want to persist is persisted only as long as it's needed. If that's forever, then a globel or a variable in a closure works well in any case. If that's not forever, then I make sure I null it out when I know I'm not going to use it any more. It I can't know when that is beforehand, then that means that I'll have to keep it alive "forever" and for ie6's sake, clean it up when the user leaves the page.
    liorean <[lio@wg]>
    Articles: RegEx evolt wsabstract , Named Arguments
    Useful Threads: JavaScript Docs & Refs, FAQ - HTML & CSS Docs, FAQ - XML Doc & Refs
    Moz: JavaScript DOM Interfaces MSDN: JScript DHTML KDE: KJS KHTML Opera: Standards

  • #4
    WA
    WA is offline
    Administrator
    Join Date
    Mar 2002
    Posts
    2,596
    Thanks
    2
    Thanked 19 Times in 18 Posts
    Thanks guys!
    Actually, in both cases you are creating a closure over the init function's local scope.
    You're right of course, didn't notice that until I looked more closely. Another question. If a method contains a closure and the method calls itself repeatedly, are new copies of the involved variables saved each time (hence taking up more and more memory) in this case, or are they just updated once the first initial copies of the variables are saved? For example, something like in the below case, with the variables in red:

    Code:
    myEffect.fadeup=function(obj, direction){
    	var target=obj
    	var fadeamount=(direction=="fadeup")? 0.1 : -0.1
    	if (direction=="fadeup" && target.opacity<1){
    		target.style.opacity+=fadeamount
    		setTimeout(function(){myEffect.fadeup(obj, direction)}, 50)
    	}
    }
    - George
    - JavaScript Kit- JavaScript tutorials and 400+ scripts!
    - JavaScript Reference- JavaScript reference you can relate to.

  • #5
    Regular Coder
    Join Date
    Jun 2007
    Location
    USA
    Posts
    527
    Thanks
    26
    Thanked 74 Times in 72 Posts
    It will create a new closure, but it will be available for garbage collection in no time. It won't close over the function itself (funcA) . . . just its locals (locsA). So when the recursed version, funcB is called, funcA is gone, but locsA are still there. But when funcB creates funcC for recusion, and when funcC comes by, locsA are going to be gone, but locsB are going to be around. Etc.

    Closures happen at the lexical level, meaning they won't chain.

    The short of it is, your heap won't explode.
    Last edited by Trinithis; 05-02-2008 at 09:46 PM.
    Trinithis

  • #6
    Master Coder
    Join Date
    Feb 2003
    Location
    UmeŚ, Sweden
    Posts
    5,575
    Thanks
    0
    Thanked 83 Times in 74 Posts
    Quote Originally Posted by WA View Post
    Another question. If a method contains a closure and the method calls itself repeatedly, are new copies of the involved variables saved each time (hence taking up more and more memory) in this case, or are they just updated once the first initial copies of the variables are saved?
    All closures refer to the same variables. This means that no additional memory is used for them no matter how many closures you take, except for the memory needed to store the function object itself. Note that if one of the closures changes a closed over variable, then that change will be seen in all the closures because they are all referring to the same variable, not copies of it.
    For example, something like in the below case, with the variables in red:

    Code:
    myEffect.fadeup=function(obj, direction){
    	var target=obj
    	var fadeamount=(direction=="fadeup")? 0.1 : -0.1
    	if (direction=="fadeup" && target.opacity<1){
    		target.style.opacity+=fadeamount
    		setTimeout(function(){myEffect.fadeup(obj, direction)}, 50)
    	}
    }
    Every time through this, you're creating a new closure and throwing away the old one. Unless you save other closures over the same scope, the old closure will be garbage to be collected on the next gc run, and a new closure is created each time through.


    Another thing you might want to take note of: Try running this code:
    Code:
    var
        i=0,
        j=0;
    while(i<1)
         i=i+0.1,
         ++j;
    alert('After finishing the loop\n\ni: '+i+'\nj: '+j);
    When looping over fractional numbers, you're best off converting the loop into one that uses only integers instead.
    liorean <[lio@wg]>
    Articles: RegEx evolt wsabstract , Named Arguments
    Useful Threads: JavaScript Docs & Refs, FAQ - HTML & CSS Docs, FAQ - XML Doc & Refs
    Moz: JavaScript DOM Interfaces MSDN: JScript DHTML KDE: KJS KHTML Opera: Standards


  •  

    Posting Permissions

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