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.
Page 1 of 3 123 LastLast
Results 1 to 15 of 37
  1. #1
    Regular Coder
    Join Date
    Jun 2006
    Location
    UK
    Posts
    907
    Thanks
    301
    Thanked 2 Times in 2 Posts

    Question Calling a function only once

    Hi

    I am trying to write a function that will generate a random number b/w 1-10 but what I also want is that the function should return the random number only once (no matter how many times we call it) unless we force it to generate it using a flag or something..I am stuck here, so please can someone help me?

    herez the code
    Code:
    var a = function(){
      	return Math.floor(Math.random() * (10 - 1) + 1); 
      
    };
    
    var b = a();
    
    console.log(b);
    JSBIN:
    http://jsbin.com/uMUhoJU/2/edit


    Thanks

  • #2
    Regular Coder
    Join Date
    Jan 2013
    Location
    Germany
    Posts
    578
    Thanks
    4
    Thanked 77 Times in 77 Posts
    You can use a nice closure for this. Using a self-invoked function you can "trap" the generated number in a closure so that it stays accessible. Then it simply is a matter of checking whether the number has been initialized before. If not, it is created randomly. If the additional flag is set, the value will be reset so that it will be created again.

    Code:
    var generateRandomNumber = (function () {
        var generatedNumber = null;
        return function (forceRefresh) {
            if(forceRefresh) {
                generatedNumber = null;
            }
    
            generatedNumber = generatedNumber !== null ? generatedNumber : Math.floor(Math.random() * (10 - 1) + 1);
            return generatedNumber;
        };
    })();
    
    generateRandomNumber(); // e.g. 3
    generateRandomNumber(); // 3
    generateRandomNumber(); // 3
    generateRandomNumber(); // 3
    generateRandomNumber(true); // e.g. 5
    generateRandomNumber(); // 5
    generateRandomNumber(); // 5
    Last edited by Airblader; 09-29-2013 at 09:36 AM.

  • Users who have thanked Airblader for this post:

    phantom007 (09-29-2013)

  • #3
    Regular Coder
    Join Date
    Jun 2006
    Location
    UK
    Posts
    907
    Thanks
    301
    Thanked 2 Times in 2 Posts
    Hi

    Thanks for the reply but it does not seem to be working.

    http://jsbin.com/uMUhoJU/3/edit?js,console

    output:
    Code:
    6
    7
    6
    5
    5
    6
    8
    8

  • #4
    Regular Coder
    Join Date
    Jan 2013
    Location
    Germany
    Posts
    578
    Thanks
    4
    Thanked 77 Times in 77 Posts
    It works fine, but if you keep pressing "Run" on JSBin it won't work because it always refreshes the page. Just copy the function into your browser's console and repeatedly call the function to see that it works. Or check this out: http://jsbin.com/uMUhoJU/5/edit?html,js,output

  • Users who have thanked Airblader for this post:

    phantom007 (09-29-2013)

  • #5
    Regular Coder
    Join Date
    Jun 2006
    Location
    UK
    Posts
    907
    Thanks
    301
    Thanked 2 Times in 2 Posts

    Question

    Yes you are so right..thanks for clarifying that. I do have a few questions now...hope you wont mind answering

    1) The term "closure" u mentioned is new to me, i tried reading it on wikipedia but what they explained didnt make any sense to me, may be because its too complicated. Could you please explain the meaning of closure in layman's terms?

    2) I noticed u added (); at the end of the function, what is it for?

    3) In your code, you are checking the "forceRefresh" variable and assigning null to generatedNumber but nowhere in ur code u have defined "forceRefresh", any reason why?


    Many thanks for your help

  • #6
    Regular Coder
    Join Date
    Jan 2013
    Location
    Germany
    Posts
    578
    Thanks
    4
    Thanked 77 Times in 77 Posts
    I'll try to break it down. The important concept you need to understand is what a self-invoked function is. In Javascript, you can define a function that is immediately invoked by wrapping it in parentheses and then call it like we call any function: by adding "()"

    Code:
    (function () {})();
    For example, this will do nothing:

    Code:
    function () { alert("Hello World"); }
    Why not? Because no one ever invokes this function. But if we make it self-invoked, it will show the alert:

    Code:
    (function () { alert("Hello World"); })();
    Now, the thing why this is useful is that it introduces a scope (Javascript has function-scope). So we can define a variable inside our function:

    Code:
    (function () { var someVariable = 42; })();
    This variable, however, is only accessible from within the function (since it is local). So far, this is still not useful. But this is a function, and functions can return things, right? And since we're really crazy: Functions can even return functions! Let's do that:

    Code:
    var result = (function () {
        var someVariable = 42;
        return function () {
            return someVariable;
        };
    })();
    Now, bare with me. The self-invoked function has a local variable and then returns a new function. Since the returned function is nested, it also has access to the local variable and simply returns it. We then assign the outcome (which is a function!) to the variable result. This means that result is now a function that will always return 42:

    Code:
    result(); // 42
    result(); // 42
    What happened here is that we created a closure. What does that mean? The self-invoked function itself is never referenced again and could be garbage-collected, but it returns a function referencing its local variable. This forces the variable (and therefore the closure) to stay alive.

    Now so far this should answer your first and second question (if not, just read it again carefully and try to understand the examples). As for the last question, you have to look at my code again: forceRefresh is declared as the argument of the function that will be returned from the self-invoked function. This means that generateRandomNumber will effectively be a function that takes the forceRefresh argument.

    So to wrap it up: We create an outer function that returns an inner function and immediately invoke the outer function, which allows the inner function to keep referencing the variable of the outer function (this is basically what a closure is). It's confusing at first, but really there is nothing to be scared of. At the end of the day, this involves no new concepts, it simply is calling a function which will return a function. It just needs a little bit to wrap your head around it, but then it's really not that complicated.
    Last edited by Airblader; 09-29-2013 at 10:06 AM.

  • Users who have thanked Airblader for this post:

    phantom007 (09-29-2013)

  • #7
    Regular Coder
    Join Date
    Jan 2013
    Location
    Germany
    Posts
    578
    Thanks
    4
    Thanked 77 Times in 77 Posts
    A better read on closures than on wikipedia might be the Mozilla MDN since it is specifically about Javascript: https://developer.mozilla.org/en-US/...Guide/Closures

  • Users who have thanked Airblader for this post:

    phantom007 (09-29-2013)

  • #8
    Regular Coder
    Join Date
    Jun 2006
    Location
    UK
    Posts
    907
    Thanks
    301
    Thanked 2 Times in 2 Posts
    cool!

    Thanks for the detailed explanation.

    I am trying to implement your logic in a object literal but all it returns is the function itself instead of teh random value, could u pls check the code below and let me know what am i missing?

    http://jsbin.com/uMUhoJU/6/edit?html,js,output


    Thanks

  • #9
    Regular Coder
    Join Date
    Jan 2013
    Location
    Germany
    Posts
    578
    Thanks
    4
    Thanked 77 Times in 77 Posts
    Instead of posting codes, this time I will try with the explanation to see if you can fix it yourself:

    1) The actual mistake: You forgot to actually invoke the outer function (to make it self-invoked).
    2) Just an additional thing: You don't want/need to declare forceRefresh as an argument of the outer function, it has no place of being there.

    Maybe a little addition to 2): The outer function is a function that is called immediately anyway. This is what the last "()" in my code did. It just a completely normal(!) function call. And since "()" means it is called with no arguments, any arguments declared in the outer function will only ever be undefined anyway:

    Code:
    var result = (function (someVariable) {
        return function (anotherVariable) {
            alert('someVariable = ' + someVariable + ', anotherVariable = ' + anotherVariable);
        };
    })();
    
    result(42); // alerts 'someVariable = undefined, anotherVariable = 42'
    Of course you could pass an argument to the outer function as well:

    Code:
    var result = (function (someVariable) {
        return function (anotherVariable) {
            alert('someVariable = ' + someVariable + ', anotherVariable = ' + anotherVariable);
        };
    })(9001);
    
    result(42); // alerts 'someVariable = 9001, anotherVariable = 42'
    result(1337); // alerts 'someVariable = 9001, anotherVariable = 1337'
    But as you can see, the 9001 will be fixed and not changable anymore since it was only set once when the self-invoked function was invoked.
    Last edited by Airblader; 09-29-2013 at 10:34 AM.

  • Users who have thanked Airblader for this post:

    phantom007 (09-29-2013)

  • #10
    Regular Coder
    Join Date
    Jun 2006
    Location
    UK
    Posts
    907
    Thanks
    301
    Thanked 2 Times in 2 Posts
    ok I was able to fix it with your help.

    Pls chk
    http://jsbin.com/uMUhoJU/7/edit


    Just one thing I could not understand is, when I call obj.random(true); how come its able to get "true" in the parameter while there is no parameter in the outer function?


    Thanks

  • #11
    Regular Coder
    Join Date
    Jan 2013
    Location
    Germany
    Posts
    578
    Thanks
    4
    Thanked 77 Times in 77 Posts
    Quote Originally Posted by cancer10 View Post
    Just one thing I could not understand is, when I call obj.random(true); how come its able to get "true" in the parameter while there is no parameter in the outer function?
    Because what is actually assigned to obj.random is the inner function. Remember, the outer function is already being invoked, which means that obj.random will be whatever that outer function returned – which is the inner function!

    The outer function is simply there to create a closure so that we can persist the already generated number (which would otherwise not be possible without storing it in a higher scope – it's just a neat way to keep variables as local and hidden as possible)

  • Users who have thanked Airblader for this post:

    phantom007 (09-29-2013)

  • #12
    Regular Coder
    Join Date
    Jan 2013
    Location
    Germany
    Posts
    578
    Thanks
    4
    Thanked 77 Times in 77 Posts
    By the way: You aren't using the "var" keyword for your variables. This declares every variable as global, which is really bad practice. Don't ever declare variables without the "var" keyword! It will cause you a lot of headache very quickly.

  • Users who have thanked Airblader for this post:

    phantom007 (09-29-2013)

  • #13
    Regular Coder
    Join Date
    Jun 2006
    Location
    UK
    Posts
    907
    Thanks
    301
    Thanked 2 Times in 2 Posts
    Quote Originally Posted by Airblader View Post
    (which would otherwise not be possible without storing it in a higher scope – it's just a neat way to keep variables as local and hidden as possible)
    So you mean so say that we could have accomplished this without using a closure but declaring a global variable window.forceRefresh = true/false ?

    Quote Originally Posted by Airblader View Post
    By the way: You aren't using the "var" keyword for your variables. This declares every variable as global, which is really bad practice. Don't ever declare variables without the "var" keyword! It will cause you a lot of headache very quickly.
    Yes, i missed adding var before generateRandomNumber, right?

    var generateRandomNumber = null;

  • #14
    Regular Coder
    Join Date
    Jan 2013
    Location
    Germany
    Posts
    578
    Thanks
    4
    Thanked 77 Times in 77 Posts
    Quote Originally Posted by cancer10 View Post
    So you mean so say that we could have accomplished this without using a closure but declaring a global variable window.forceRefresh = true/false ?
    Yes, and generatedNumber could be global as well. It would make the code look a lot simpler at first, but please don't resort to doing that. Variables should always be as local as possible, otherwise your code will get messy and hard to maintain quickly. You will find yourself experiencing a lot of weird behavior and having a hard time figuring out why that's the case. The nice thing about local variables is that you know exactly that its vaue can only be modified from within the same scope (= same function). Global variables could be modified literally anywhere else in the code, even accidentally by other scripts. So effectively they destroy your own script, they destroy other scripts and they lead to other scripts destroying your script.

    Global variables are amongst the most evil things in the entire language.

    That being said: Even without global variables this could be written without using a closure like this. But in this particular case, a closure just seems to be the best choice.

    Yes, i missed adding var before generateRandomNumber, right?
    The obj object itself was declared global, too.
    Last edited by Airblader; 09-29-2013 at 11:00 AM.

  • Users who have thanked Airblader for this post:

    phantom007 (09-29-2013)

  • #15
    Regular Coder
    Join Date
    Jun 2006
    Location
    UK
    Posts
    907
    Thanks
    301
    Thanked 2 Times in 2 Posts
    Quote Originally Posted by Airblader View Post
    The obj object itself was declared global, too.
    Yes, I did that deliberately since the obj var is declared inside a function and the obj var needs to be accessed from a diff function which is why I had to declare it as global.

    Thanks


  •  
    Page 1 of 3 123 LastLast

    Posting Permissions

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