0

I'm trying to understand this JavaScript code produced by CoffeeScript.

I'm used to seeing functions defined as:

  • function Animal(name) {...}
  • var Animal = function(name){...};

But CoffeeScript produces the following:

var Animal = (function() {
  function Animal(name) {
    this.name = name;
  }

  return Animal;

})();

Questions:

  • What does creating a "named" function inside an anonymous function do?
  • What are the advantages of defining the Animal function like this vs. one of the two bulleted ways?

Bonus Question Are these equivalent?

//Methodology #1
function Animal(name) {...}
//Methodology #2
var Animal = function Animal(name) {...};
Snekse
  • 15,474
  • 10
  • 62
  • 77

4 Answers4

1

What are the advantages of defining the Animal function like this vs. one of the two bulleted ways?

  1. The function will still have a name which shows up in the debugging console
  2. Despite having a name, the function will not pollute the global namespace, since it's scope is local to the anonymous containing function, and it is being made available as a closure.

Regarding your bonus question:

http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/

Jonah
  • 15,806
  • 22
  • 87
  • 161
1
  1. What does creating a "named" function inside an anonymous function do?

    UPDATE

    This is only true for IE8 because of the JScript Bug, in modern (and sane) browsers Animal is not registered in the context namespace when using a named function expression.

    It prevents the function declaration from registering the function name in the context (which might be the global namespace). That means that if you would do this:

    var Tiger = function Animal() {};
    

    Both Tiger and Animal get registered in the global namespace, whereas this:

    var Tiger = function() { function Animal() {}; return Animal; };
    

    only registers Animal in the global namespace.

  2. What are the advantages of defining the Animal function like this vs. one of the two bulleted ways?

    UPDATE

    Again, this is not valid if working for more modern browsers than IE8. In these cases, you can use the named function expression without worrying about your context.

    The advantage is that while keeping the context (again, which might be the global) namespace clean, you can still have a named constructor for your objects. That means:

    var Tiger = function() {};
    var t = new Tiger();
    t.constructor // is an anonymous function() {}
    

    is less declarative than

    var Tiger = function() { function Animal() {}; return Animal };
    var t = new Tiger();
    t.constructor // is a function Animal()
    Animal //=> is not defined
    

    and this pollutes the global namespace or the context namespace with the potentially unneeded registration of Animal

    var Tiger = function Animal() {};
    var t = new Tiger();
    t.constructor // is a function Animal()
    Animal //=> returns function Animal();
    
  3. Are these equivalent?

    UPDATE

    And this is true for all browsers.

    No. In the first, you declare a function by the name of Animal, whereas in the second, you assign a function declared by the name of Animal to a variable with the name of Animal, which is known as a named function expression. In the first, the lookup works via the function name, in the second, the lookup works via the variable name. This is because function declarations can not overwrite function expressions: The function declaration is loaded in the context on script load, whereas the expression assignment is executed when the script is executed:

    function a() { 
      var b = function() { return 3; }; 
      return b(); 
      function b() { return 9; } 
    }
    
    a() //=> returns 3;
    
Community
  • 1
  • 1
Beat Richartz
  • 9,474
  • 1
  • 33
  • 50
  • 1 is incorrect. Named function expressions do not turn their names into variables. That pretty much makes the rest of the answer invalid. – cHao Apr 22 '13 at 16:29
  • @cHao I didn't say it leaks into the outer scope, I just meant that they are registered in the given context (which may be the global namespace). Reformulated it so it should be clear now. – Beat Richartz Apr 22 '13 at 16:32
  • Still incorrect. When i say `var x = function y() { };` i can see `x`, but `y` does not exist anywhere but *within* the function. – cHao Apr 22 '13 at 16:33
  • That is, `var x = function y() { y(); }; x();` works (aside from overflowing the call stack), whereas `var x = function y() { }; y();` doesn't run at all. ("ReferenceError: y is not defined", says node.js and Chrome.) – cHao Apr 22 '13 at 16:40
  • @cHao go to IE8 and see for yourself. [Here's some more quirks to think of](http://kangax.github.io/nfe/) I updated the question accordingly. – Beat Richartz Apr 22 '13 at 16:58
  • Yeah, i'd noticed IE8's behavior as i was testing other browsers. :P Seems it's the last version that does that; IE9 fixed that particular bug (long as you're not in quirks mode). – cHao Apr 22 '13 at 17:11
  • @cHao Sad thing is that this bug became so usual for me now I forgot that it actually *is* a bug and not standard JS. So thanks for reminding me :) – Beat Richartz Apr 22 '13 at 17:16
0

"What does creating a "named" function inside an anonymous function do?"

It simply creates a new function that is local to the variable scope inside the function in which it is nested.

"What are the advantages of defining the Animal function like this vs. one of the two bulleted ways?"

Given the code shown, there's no particular advantage, but if there were other variables inside that outer function, that inner function could reference them providing a controlled access to them (since the variables would be otherwise inaccessible from outside that outer function).

This is possible because JavaScript functions form what's called a closure, meaning they carry their original variable scope with them even when removed from that variable scope.

"Bonus Question Are these equivalent?"

To the original code? Yes, they're basically equivalent.

To each other, mostly. Main difference is that function declarations (Method 1) are "hoisted", meaning they're available at the top of their scope, so you can actually use them before they're defined.

Function expressions are only available after the expression has been evaluated.


Note that all the functions have names, and so they'll all show up in debugging console. Also, all the functions end up in the same variable scope.

silly little me
  • 581
  • 3
  • 4
0

Are you familiar with the OOP (Object Oriented Programming)?

Because right here what you see is an attempt to define an object in JavaScript, even though there is no class keyword in JavaScript.

If you know a little bit about Java (which has just about nothing to do with JavaScript), then this should look familiar:

public class Animal { // creating a class
    private String name; // declaring its attributes

    public Animal(String name) { // declaring constructor
        this.name = name; // assigning value to the attribute
    }
}

The JavaScript code you gave does basically the same thing, e.g creating a class (some kind of enhanced type), that has both attributes (variables assigned to it) and methods (functions assigned to it).

So this code

function Animal(name) {
    this.name = name;
}

creates the Animal class.

The thing is that in JavaScript, there isn’t really any concept of class, but only of objects. So right here you create a variable and then you assign the result of the function that is following:

var Animal = (function() {
    // some code here
})
(); // note the parenthesis here that means the function is called

Then, a constructor is defined, and the object containing this function is returned.

So at the end of the execution, the var Animal contains a constructor, and thus any object can be initialized like this: ̀var myAnimal = new Animal('Panther');.

Hope that helps.

Gus Monod
  • 110
  • 8