Javascript closures function parameters?

后端 未结 3 1573
不思量自难忘°
不思量自难忘° 2021-02-09 02:47

Code belongs to javascriptissexy.com My question is why invoking mjName (\"Jackson\") returns \"This celebrity is Michael Jackson\"? Is it that second parameter given in ANY out

3条回答
  •  终归单人心
    2021-02-09 03:42

    The OP asked for an explanation of the Whole concept in detail. The attempt here is to describe the core elements that are necessary for closures to occur.

    I think that part of the confusion with examples like the one from javascriptissexy is that the names of these functions do not clearly represent what they are supposed to do, especially to someone who is new to javascript or new coding in general.

    Lets start by talking about scope:

    In Javascript, every function creates its own local scope or memory space. This is called Lexical Scoping. This memory space stores all of the variables from the functions's parameters as well as the declared variables and expressions within the function body (inside the curly braces).

    As seen in the example from javascriptissexy, we can nest functions. Since each function creates its own local scope, we need to understand how these scopes relate and interact with each other. There are three different types of relationships that scopes can have.

    I would encourage you to test all of these code snippets inside your browser dev console:

    Child scopes have access to their parent's (and grandparent's, great grandparent's etc...) scope variables

        function parent() {
    
            var parentAsset = 'The Minivan'
    
            function child() {
    
                //child has access to parent asset
    
                console.log(parentAsset);
    
            }
    
            // call child function
    
            child();
        }
    
        parent();  // 'The Minivan'
    

    Parent scopes DO NOT have access to their children's scope variables

        function parent() {
    
            function child() {
    
                var childAsset = 'Mp3 Player'
    
            }
    
            //parent can't get childAsset
    
            console.log(childAsset);
    
        }
    
        parent();   // ReferenceError childAsset not defined
    

    Sibling scopes DO NOT have access to each other's scope variables

        function childOne() {
    
            var childOneAsset = 'Iphone'
    
        }
    
        function childTwo() {
    
            console.log(childOneAsset);
    
        }
    
        childTwo();  // ReferenceError childOneAsset not defined
    

    Okay, so back to the function mentioned by OP. Let's try to remake this function with better names. I am adding one more variable to this first example function to show a point.

    Here are 4 things that happen when you call getFirstName('Michael') in the example below:

    1. Within this function, the variable firstName is set to 'Michael'
    2. var nameIntro is set it to the value "This celebrity is "
    3. var unusedString is set to the value "This string will be garbage collected"
    4. The function introduceCelebrity is declared
    5. The function introduceCelebrity is returned

      function getFirstName (firstName) {
      
          var nameIntro = "This celebrity is ";
          var unusedString = "This string will be garbage collected";
      
          function introduceCelebrity (lastName) {
              return nameIntro + firstName + " " + lastName;
          }
      
          return introduceCelebrity;
      }
      
      var mjName = getFirstName('Michael');
      

    You probably already knew that.

    Here are some interesting things to note:

    • The getFirstName function does nothing with firstName or nameIntro other than set their values. So there is no magic there.
    • The child function introduceCelebrity references those two variables. As mentioned before, it can do that because children scopes can access parent scope variables. This is the first important step to a closure.
    • The introduceCelebrity function is then returned (but not executed), presumably so we can call it at a later time. This is the second step to a closure.
    • Because introduceCelebrity references parent scope variables, and we return the whole function, the javascript runtime maintains a pointer to those variables, even after the getFirstName function returns.
    • Because that pointer exists, the garbage collector leaves those variables alone. If those pointers didn't exist, the garbage collector would come through and clean those memory addresses and those values would be inaccessible.
    • The unusedString variable is not referenced in the child function, therefore it is garbage collected and is no longer available.

    So let's look at the code again:

    function getFirstName (firstName) {
    
        var nameIntro = "This celebrity is ";
    
        function introduceCelebrity (theLastName) {
            return nameIntro + firstName + " " + theLastName;
        }
        return introduceCelebrity;
    }
    
    var mjName = getFirstName('Michael');
    

    When this code executes, we are basically doing this:

    var mjName = function(theLastName) {
        return nameIntro + firstName + " " + theLastName;
    }
    

    What is special about this? Where is the closure?

    Because our getFirstName function has been executed, we might think that the whole thing has gone away along with its local variables or assets. THIS IS INCORRECT.

    We created a closure by referencing parent scope variables inside a child function and returning the child function. So really, the new scope of the code right above actually looks more like this:

    var nameIntro = "This celebrity is ";
    
    var firstName = "Michael"
    
    var mjName = function(theLastName) {
        return nameIntro + firstName + " " + theLastName;
    }
    

    See how nameIntro and firstName are now available to us? THAT IS BECAUSE WE CREATED CLOSURE.

    So now we call mjName:

    mjName('Jackson');  // 'This celebrity is Michael Jackson'
    

    And we get the result expected.

    Wait, One last thing!

    To really drive the point home, lets compare our example to a slightly modified one.

    Notice the original function is nested. Closures only happen with nested functions.

    function getFirstName (firstName) {
    
        var nameIntro = "This celebrity is ";
    
        function introduceCelebrity (theLastName) {
            return nameIntro + firstName + " " + theLastName;
        }
        return introduceCelebrity;
    }
    
    var mjName = getFirstName('Michael');
    

    Let's try removing that nesting:

    function getFirstName (firstName) { 
        var nameIntro = "This celebrity is ";
    }
    
    function introduceCelebrity (theLastName) {
        return nameIntro + firstName + " " + theLastName;
    }
    
    var mjName = getFirstName('Michael');
    introduceCelebrity('Jackson');  
    
    // ReferenceError: nameIntro is not defined
    

    Would this work?

    No, it wouldn't. Because sibling scopes can't access each other's variables.

    How could we get this to work without a closure then?

    1. getFirstName must return an object or array with our variables
    2. we must set getFirstName('Michael') to a global variable mjName
    3. Call introduceCelebrity('Jackon'), passing in the values we mjName

      function getFirstName (firstName) { 
      
          var nameIntro = "This celebrity is ";
      
          return {
              firstName: firstName,
              nameIntro: nameIntro
          }
      }
      
      var mjName = getFirstName('Michael');  // returns our object
      
      function introduceCelebrity (theLastName, firstName, nameIntro) {
          return nameIntro + firstName + " " + theLastName;
      }
      
      introduceCelebrity('Jackson', mjName.firstName, mjName.nameIntro);  
      
      // 'This celebrity is Michael Jackson'
      

提交回复
热议问题