Categories
function idioms javascript syntax

var functionName = function() {} vs function functionName() {}

7452

I’ve recently started maintaining someone else’s JavaScript code. I’m fixing bugs, adding features and also trying to tidy up the code and make it more consistent.

The previous developer used two ways of declaring functions and I can’t work out if there is a reason behind it or not.

The two ways are:

var functionOne = function() {
    // Some code
};
function functionTwo() {
    // Some code
}

What are the reasons for using these two different methods and what are the pros and cons of each? Is there anything that can be done with one method that can’t be done with the other?

0

    5469

    The difference is that functionOne is a function expression and so only defined when that line is reached, whereas functionTwo is a function declaration and is defined as soon as its surrounding function or script is executed (due to hoisting).

    For example, a function expression:

    // TypeError: functionOne is not a function
    functionOne();
    
    var functionOne = function() {
      console.log("Hello!");
    };

    And, a function declaration:

    // Outputs: "Hello!"
    functionTwo();
    
    function functionTwo() {
      console.log("Hello!");
    }

    Historically, function declarations defined within blocks were handled inconsistently between browsers. Strict mode (introduced in ES5) resolved this by scoping function declarations to their enclosing block.

    'use strict';    
    { // note this block!
      function functionThree() {
        console.log("Hello!");
      }
    }
    functionThree(); // ReferenceError

    5

    • Function definitions are executed when code enters the surrounding block, rather than when it enters the enclosing function. I don’t know if things always worked that way, but it would be unavoidable if a block used let or const to define a variable that was closed over by a function within it, and applying that rule consistently is probably better than applying it only when unavoidable.

      – supercat

      Oct 26, 2020 at 21:37

    • 24

      The sentence “due to hoisting” might give a wrong impression that only the named function gets hoisted. In fact, both var functionOne as well as function functionTwo get hoisted to some degree – it’s just that functionOne is set to undefined (you could call it half-hoisting, variables always get hoisted only to that degree) whereas function functionTwo is fully hoisted in that it’s defined and declared. Invoking something that’s undefined will of course then throw a typeError.

      Dec 4, 2020 at 19:19


    • 2

      There is also a slight variation of var case, when one uses let functionFour = function () {...}. In this case declaration of let functionFour is hoisted. But it does not get initialized, not even with an undefined value. So it produces a slightly different error: Uncaught ReferenceError: Cannot access ‘functionFour’ before initialization The same holds for const.

      Jan 5 at 15:11


    • @rails_has_elegance so what’s the point of calling it “half hoisted” if it acts exactly the same as “not hoisted at all”?

      – vanowm

      Apr 2 at 19:57

    • 1

      @vanowm it doesn’t act the same as “not hoisted at all” though. If it wasn’t hoisted you’d get a ReferenceError. Since it’s hoisted you get a TypeError. Compare these two statements in a console: 1. hoisted(); var hoisted = function() {} 2. notHoisted(); const notHoisted = function() {}. In the first case, it’s a TypeError because you’re trying to invoke undefined (it DID get hoisted though, that’s why it’s at least undefined, which is still more than nothing at all). In the second case, it’s not even undefined, you just get a plain ReferenceError.

      Apr 14 at 14:12


    2070

    First I want to correct Greg: function abc(){} is scoped too — the name abc is defined in the scope where this definition is encountered. Example:

    function xyz(){
      function abc(){};
      // abc is defined here...
    }
    // ...but not here
    

    Secondly, it is possible to combine both styles:

    var xyz = function abc(){};
    

    xyz is going to be defined as usual, abc is undefined in all browsers but Internet Explorer — do not rely on it being defined. But it will be defined inside its body:

    var xyz = function abc(){
      // xyz is visible here
      // abc is visible here
    }
    // xyz is visible here
    // abc is undefined here
    

    If you want to alias functions on all browsers, use this kind of declaration:

    function abc(){};
    var xyz = abc;
    

    In this case, both xyz and abc are aliases of the same object:

    console.log(xyz === abc); // prints "true"
    

    One compelling reason to use the combined style is the “name” attribute of function objects (not supported by Internet Explorer). Basically when you define a function like

    function abc(){};
    console.log(abc.name); // prints "abc"
    

    its name is automatically assigned. But when you define it like

    var abc = function(){};
    console.log(abc.name); // prints ""
    

    its name is empty — we created an anonymous function and assigned it to some variable.

    Another good reason to use the combined style is to use a short internal name to refer to itself, while providing a long non-conflicting name for external users:

    // Assume really.long.external.scoped is {}
    really.long.external.scoped.name = function shortcut(n){
      // Let it call itself recursively:
      shortcut(n - 1);
      // ...
      // Let it pass itself as a callback:
      someFunction(shortcut);
      // ...
    }
    

    In the example above we can do the same with an external name, but it’ll be too unwieldy (and slower).

    (Another way to refer to itself is to use arguments.callee, which is still relatively long, and not supported in the strict mode.)

    Deep down, JavaScript treats both statements differently. This is a function declaration:

    function abc(){}
    

    abc here is defined everywhere in the current scope:

    // We can call it here
    abc(); // Works
    
    // Yet, it is defined down there.
    function abc(){}
    
    // We can call it again
    abc(); // Works
    

    Also, it hoisted through a return statement:

    // We can call it here
    abc(); // Works
    return;
    function abc(){}
    

    This is a function expression:

    var xyz = function(){};
    

    xyz here is defined from the point of assignment:

    // We can't call it here
    xyz(); // UNDEFINED!!!
    
    // Now it is defined
    xyz = function(){}
    
    // We can call it here
    xyz(); // works
    

    Function declaration vs. function expression is the real reason why there is a difference demonstrated by Greg.

    Fun fact:

    var xyz = function abc(){};
    console.log(xyz.name); // Prints "abc"
    

    Personally, I prefer the “function expression” declaration because this way I can control the visibility. When I define the function like

    var abc = function(){};
    

    I know that I defined the function locally. When I define the function like

    abc = function(){};
    

    I know that I defined it globally providing that I didn’t define abc anywhere in the chain of scopes. This style of definition is resilient even when used inside eval(). While the definition

    function abc(){};
    

    depends on the context and may leave you guessing where it is actually defined, especially in the case of eval() — the answer is: It depends on the browser.

    7

    • var abc = function(){}; console.log(abc.name); // “abc” // from 2021

      – lfx_cool

      Apr 3, 2021 at 16:27


    • 3

      Apparently, the JS runtime became smarter. Yet wrap it up and: var abc = (() => function(){})(); console.log(abc.name); // nothing

      Apr 15, 2021 at 1:17


    • @EugeneLazutkin you are executing the function and trying to read the name of the result. Remove the ‘();’ part and yours example will e correct 😉

      – ikirachen

      Oct 7, 2021 at 16:51

    • @EugeneLazutkin you are defining a function and immediately invoking (calling) it, also called an IIFE (Immediately Invoked Function Expression), which is one method of implementing lexical scoping (nothing from inside the IIFE will be accessible outside of it). So the value of abc is not the function itself but rather that function’s return value. It makes sense for abc.name to be empty, because abc returns an unnamed function. @ikirachen mentioned removing the () because that is what is invoking the function. Without that, it’s just wrapped in superfluous parentheses.

      – Sinjai

      Nov 16, 2021 at 23:43

    • To be clear, it’s a way to implement tighter scoping in that variables declared within those parentheses using var will be function-scoped as usual, but that anonymous function is no longer accessible outside of the parentheses it’s wrapped in. Thankfully these days we have let, which uses the block scoping the average (sane) person would expect. It’s best to pretend var doesn’t exist, in my opinion.

      – Sinjai

      Nov 16, 2021 at 23:48

    718

    +200

    Here’s the rundown on the standard forms that create functions: (Originally written for another question, but adapted after being moved into the canonical question.)

    Terms:

    The quick list:

    • Function Declaration

    • “Anonymous” function Expression (which despite the term, sometimes create functions with names)

    • Named function Expression

    • Accessor Function Initializer (ES5+)

    • Arrow Function Expression (ES2015+) (which, like anonymous function expressions, don’t involve an explicit name, and yet can create functions with names)

    • Method Declaration in Object Initializer (ES2015+)

    • Constructor and Method Declarations in class (ES2015+)

    Function Declaration

    The first form is a function declaration, which looks like this:

    function x() {
        console.log('x');
    }
    

    A function declaration is a declaration; it’s not a statement or expression. As such, you don’t follow it with a ; (although doing so is harmless).

    A function declaration is processed when execution enters the context in which it appears, before any step-by-step code is executed. The function it creates is given a proper name (x in the example above), and that name is put in the scope in which the declaration appears.

    Because it’s processed before any step-by-step code in the same context, you can do things like this:

    x(); // Works even though it's above the declaration
    function x() {
        console.log('x');
    }
    

    Until ES2015, the spec didn’t cover what a JavaScript engine should do if you put a function declaration inside a control structure like try, if, switch, while, etc., like this:

    if (someCondition) {
        function foo() {    // <===== HERE THERE
        }                   // <===== BE DRAGONS
    }
    

    And since they’re processed before step-by-step code is run, it’s tricky to know what to do when they’re in a control structure.

    Although doing this wasn’t specified until ES2015, it was an allowable extension to support function declarations in blocks. Unfortunately (and inevitably), different engines did different things.

    As of ES2015, the specification says what to do. In fact, it gives three separate things to do:

    1. If in loose mode not on a web browser, the JavaScript engine is supposed to do one thing
    2. If in loose mode on a web browser, the JavaScript engine is supposed to do something else
    3. If in strict mode (browser or not), the JavaScript engine is supposed to do yet another thing

    The rules for the loose modes are tricky, but in strict mode, function declarations in blocks are easy: They’re local to the block (they have block scope, which is also new in ES2015), and they’re hoisted to the top of the block. So:

    "use strict";
    if (someCondition) {
        foo();               // Works just fine
        function foo() {
        }
    }
    console.log(typeof foo); // "undefined" (`foo` is not in scope here
                             // because it's not in the same block)
    

    “Anonymous” function Expression

    The second common form is called an anonymous function expression:

    var y = function () {
        console.log('y');
    };
    

    Like all expressions, it’s evaluated when it’s reached in the step-by-step execution of the code.

    In ES5, the function this creates has no name (it’s anonymous). In ES2015, the function is assigned a name if possible by inferring it from context. In the example above, the name would be y. Something similar is done when the function is the value of a property initializer. (For details on when this happens and the rules, search for SetFunctionName in the the specification — it appears all over the place.)

    Named function Expression

    The third form is a named function expression (“NFE”):

    var z = function w() {
        console.log('zw')
    };
    

    The function this creates has a proper name (w in this case). Like all expressions, this is evaluated when it’s reached in the step-by-step execution of the code. The name of the function is not added to the scope in which the expression appears; the name is in scope within the function itself:

    var z = function w() {
        console.log(typeof w); // "function"
    };
    console.log(typeof w);     // "undefined"
    

    Note that NFEs have frequently been a source of bugs for JavaScript implementations. IE8 and earlier, for instance, handle NFEs completely incorrectly, creating two different functions at two different times. Early versions of Safari had issues as well. The good news is that current versions of browsers (IE9 and up, current Safari) don’t have those issues any more. (But as of this writing, sadly, IE8 remains in widespread use, and so using NFEs with code for the web in general is still problematic.)

    Accessor Function Initializer (ES5+)

    Sometimes functions can sneak in largely unnoticed; that’s the case with accessor functions. Here’s an example:

    var obj = {
        value: 0,
        get f() {
            return this.value;
        },
        set f(v) {
            this.value = v;
        }
    };
    console.log(obj.f);         // 0
    console.log(typeof obj.f);  // "number"
    

    Note that when I used the function, I didn’t use ()! That’s because it’s an accessor function for a property. We get and set the property in the normal way, but behind the scenes, the function is called.

    You can also create accessor functions with Object.defineProperty, Object.defineProperties, and the lesser-known second argument to Object.create.

    Arrow Function Expression (ES2015+)

    ES2015 brings us the arrow function. Here’s one example:

    var a = [1, 2, 3];
    var b = a.map(n => n * 2);
    console.log(b.join(", ")); // 2, 4, 6
    

    See that n => n * 2 thing hiding in the map() call? That’s a function.

    A couple of things about arrow functions:

    1. They don’t have their own this. Instead, they close over the this of the context where they’re defined. (They also close over arguments and, where relevant, super.) This means that the this within them is the same as the this where they’re created, and cannot be changed.

    2. As you’ll have noticed with the above, you don’t use the keyword function; instead, you use =>.

    The n => n * 2 example above is one form of them. If you have multiple arguments to pass the function, you use parens:

    var a = [1, 2, 3];
    var b = a.map((n, i) => n * i);
    console.log(b.join(", ")); // 0, 2, 6
    

    (Remember that Array#map passes the entry as the first argument, and the index as the second.)

    In both cases, the body of the function is just an expression; the function’s return value will automatically be the result of that expression (you don’t use an explicit return).

    If you’re doing more than just a single expression, use {} and an explicit return (if you need to return a value), as normal:

    var a = [
      {first: "Joe", last: "Bloggs"},
      {first: "Albert", last: "Bloggs"},
      {first: "Mary", last: "Albright"}
    ];
    a = a.sort((a, b) => {
      var rv = a.last.localeCompare(b.last);
      if (rv === 0) {
        rv = a.first.localeCompare(b.first);
      }
      return rv;
    });
    console.log(JSON.stringify(a));
    

    The version without { ... } is called an arrow function with an expression body or concise body. (Also: A concise arrow function.) The one with { ... } defining the body is an arrow function with a function body. (Also: A verbose arrow function.)

    Method Declaration in Object Initializer (ES2015+)

    ES2015 allows a shorter form of declaring a property that references a function called a method definition; it looks like this:

    var o = {
        foo() {
        }
    };
    

    the almost-equivalent in ES5 and earlier would be:

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

    the difference (other than verbosity) is that a method can use super, but a function cannot. So for instance, if you had an object that defined (say) valueOf using method syntax, it could use super.valueOf() to get the value Object.prototype.valueOf would have returned (before presumably doing something else with it), whereas the ES5 version would have to do Object.prototype.valueOf.call(this) instead.

    That also means that the method has a reference to the object it was defined on, so if that object is temporary (for instance, you’re passing it into Object.assign as one of the source objects), method syntax could mean that the object is retained in memory when otherwise it could have been garbage collected (if the JavaScript engine doesn’t detect that situation and handle it if none of the methods uses super).

    Constructor and Method Declarations in class (ES2015+)

    ES2015 brings us class syntax, including declared constructors and methods:

    class Person {
        constructor(firstName, lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    
        getFullName() {
            return this.firstName + " " + this.lastName;
        }
    }
    

    There are two function declarations above: One for the constructor, which gets the name Person, and one for getFullName, which is a function assigned to Person.prototype.

    0