Search code examples
javascriptfirst-class-functions

Javascript First Class Functions


While reading about Javascript First Class Functions here at SO, I came across this link: What is meant by 'first class object'? wherein I found this interesting example:

var men = function (objectOfAdmiration) {
  return objectOfAdmiration();
};
men.isSweetHeart = true;

var women = function (objectOfAdmiration) {
  return objectOfAdmiration();
};
women.isSweetHeart = true;

var aliens = function (objectOfAdmiration) {
  return objectOfAdmiration();
};

function like(obj){
  if (obj.isSweetHeart) {
      return function (){ return "Holy TRUE!"}; 
  }
  else {
      return function (){ return "Holy CRAP!"};
  }
}

alert("Men like women is " + men(like(women))); // -> "Holly TRUE!"
alert("Women like men is " + women(like(men))); // -> "Holly TRUE!"

alert("Men like aliens is " + men(like(aliens))); // -> "Holly CRAP!"
alert("Aliens like women is " + aliens(like(women))); // -> "Holly TRUE!" 

I need someone to explain this code, the way things r getting executed here. Especially the expression men(like(women)) and/or women(like(men)). How is this working out to produce the final result...

Thanks in advance


Solution

  • Just remember your "order of operations" from high school algebra:

    • Parenthesis
    • Exponents
    • Multiplication
    • Division
    • Addition
    • Subtraction

    Parenthesis are always evaluated first. And, in the case of an expression having nested sets of parenthesis, the inner set is evaluated before the outer ones.

    So, with:

    men(like(women))
    

    The like function is first invoked with the value of the women variable passed to it.

    Whatever that function returns is then the argument that is passed to the men function. Like this:

    men( result of calling like(women) )
    

    And, women(like(men))

    is the same idea, just reversing which one is invoked first.


    So, let's take this one step at a time.

    • It's very important that you first recognize that the men, women and alien functions are actually all identical. They simply invoke whatever you pass to them and return that result (which in this case will always be another function). You can infer this because all they do is stick a set of parenthesis on the end of the input argument. They each, therefore are expecting a function to be passed (because that's the only thing you can invoke).

    So, let's pay less attention to those functions and more attention to the like function because that is the one that returns different functions to its caller.

    Read the inline comments for explanations:

    // Function expression that sets men to hold a function 
    var men = function (objectOfAdmiration) {
      // This function is expecting a function to be passed to it
      // because all it does is invoke what is passed and only 
      // functions can be invoked.
      return objectOfAdmiration();
    };
    
    // Treat men like an object now and give it an isSweetHeart property with a value of true
    men.isSweetHeart = true;
    
    // Function expression that sets women to hold a function 
    var women = function (objectOfAdmiration) {
      // This function is expecting a function to be passed to it
      // because all it does is invoke what is passed and only 
      // functions can be invoked.
      return objectOfAdmiration();
    };
    
    // Treat men like an object now and give it an isSweetHeart property with a value of true
    women.isSweetHeart = true;
    
    // Function expression that sets alients to hold a function 
    var aliens = function (objectOfAdmiration) {
      // This function is expecting a function to be passed to it
      // because all it does is invoke what is passed and only 
      // functions can be invoked.
      return objectOfAdmiration();
    };
    
    // Function declaration for like.
    // This function is expecting an object that has an isSweetHeart property.
    // Since we have multiple types of objects that support that property, this
    // function is polymorphic
    function like(obj){
      if (obj.isSweetHeart) {
          // If the men or women objects are passed, this will be returned
          return function (){ return "Holy TRUE!"}; 
      }
      else {
          // Anything that doesn't have the isSweetHeart property or does
          // but that property has a "falsey" value will end up here
          return function (){ return "Holy CRAP!"};
      }
    }
    
    // Invoke like(woman)...
    // since the woman object does have an isSweetHeart property and that
    // property has a value of true, the function that will return "Holy TRUE!"
    // when it is invoked is returned.
    
    // That function is then passed to the men function, which recieves it under
    // the parameter name of "objectOfAdmiration".
    
    // Invoking that function outputs "Holy TRUE!"
    alert("Men like women is " + men(like(women))); // -> "Holy TRUE!"
    
    // This line does just about the same as above, but in reverse and
    // because the men and women functions are the same, the same output
    // is produced.
    alert("Women like men is " + women(like(men))); // -> "Holy TRUE!"
    
    // Same general procedure as before, but since aliens do not even 
    // have an isSweetHeart property, a different function is returned
    // for further processing.
    alert("Men like aliens is " + men(like(aliens))); // -> "Holy CRAP!"
    
    // This is essentially the same as the men(like(women)) function call
    // except that the result of like(women) is passed to aliens, which 
    // Simply invokes that function.
    alert("Aliens like women is " + aliens(like(women))); // -> "Holy TRUE!"