15

Suppose I have functions like:

function foo() {

}

function bar() {

}

I can write above as Object Literal notation:

var Baz = {
  foo: function() {
  },
 bar: function() {
 }
};

As far as I understand in the later case, an instance of Baz will be created when the script loads regardless if any Baz function is ever called. In the former case, function object is only created when that function is called. Am I correct about these assumptions?

If I am correct then the former would have higher performance (less memory) than the later in application where these functions are rarely called. But the advantage of the later is that it gives greater modularity and lower global namespace pollution.

What is your take on this from your professional experience? Is there a speed difference?

Hearaman
  • 8,466
  • 13
  • 41
  • 58
ace
  • 11,526
  • 39
  • 113
  • 193

1 Answers1

43

In the former case, function object is only created when that function is called.

No, the functions are created regardless.

Note that you can also do this:

function foo() {
}

function bar() {
}

var Baz = {
  foo: foo,
  bar: bar
};

Or this:

var Baz = (function() {
    function foo() {
    }

    function bar() {
    }

    return {
      foo: foo,
      bar: bar
    };
})();

The primary purpose of putting the functions on Baz as properties is to make them available as "methods" on Baz. This might be for convenience, for "namespacing", etc. In your first form (and my first form above), if that code is at global scope, foo and bar are added to the global scope, which can get pretty crowded pretty fast (esp. on browsers). In your second example, the only global symbol is Baz because the functions are anonymous. In my final example above, the only global symbol is Baz but the functions aren't anonymous, they have names that debuggers and stack traces can show you (which is a good thing; more here).

In terms of trying to optimize when functions get created, here's how it works: When execution enters a given context (the global context, or the context related to calling a function), these things are done:

  1. A behind-the-scenes execution context object is created.
  2. A behind-the-scenes variable object for that execution context is created.
  3. In the case of a function context:
    1. A property is added to the variable object for arguments (the array-like thing you can use to access arguments)
    2. A property is added to the variable object for each of the function's named arguments, with the value of the argument
    3. If the function has a name, its name is added as a property of the variable object and has the value of the function object.
  4. Properties are created on the variable object for each variable declared with var in the execution context; their values are initially undefined (regardless of whether the var has an initializer on it).
  5. Every function declaration in the context is processed. (Function expressions are not processed yet; more on the difference below.) A property on the variable object for each function name is created and receives the function object as its value.
  6. Step-by-step code execution begins.
    • Like all expressions, function expressions are evaluated when they're encountered in the step-by-step flow.
    • var statements that have initializers (e.g., var a = 2;) are treated exactly like assignment statements (a = 2;); the var aspect of it was done much earlier. (var is frequently misunderstood. For instance, we had this question just yesterday.)

You'll note the difference above between function declarations and function expressions. You can tell which is which by looking to see whether you're using the result as a right hand value — that is, are you assigning the result to a variable, using it as the right-hand side of a property definition in an object literal, or passing it into a function. If you are, it's a function expression. If you're not, it's a function declaration.

Function declaration example:

function foo() {
}

Function expression example:

var foo = function() {
};

Another:

var Baz = {
    foo: function() { }
};

(The foo line is a property declaration in an object literal that uses a function expression for the value.)

Named function expression example:

var f = function foo() {  // <== Don't do this (more below)
};

Named function expressions should be valid, but they're poorly-supported by implementations in the wild (particularly IE) and so for now they must be avoided. More here.

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Indeed, great explanation. If @alan wants to optimize performance, perhaps he should try using prototypes? – Kevin McTigue Apr 22 '11 at 21:11
  • Nice. Once instantiated, does object literal notation (the objects) containing functions execute faster than plain functions (global scope?) – DHorse Jul 20 '13 at 12:07
  • 2
    @DHorse: Perf questions in JS are tricky, because the engines vary so much. But if you mean the two options in the question, the answer is almost certainly no. Once the engine has the function reference, doing the call will be the same in both cases, so we have to look at how the engine gets the function reference. In the first example, to call `foo` the engine has to look up `foo` on the binding object of the execution context. In the second example, it has to look up `Baz` the same way, then look up `foo` on `Baz`, which is more steps. But I bet it doesn't make a real difference. – T.J. Crowder Jul 20 '13 at 12:44