3

In JavaScript, if I create a function expression like:

var foo = function foo() {
  return 'Hello world!';
}

Is this okay? Will there be any problems that occur? Or should I do:

var foo = function baz() {
  return 'Hello world!';
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Hadoren
  • 165
  • 1
  • 2
  • 12
  • 1
    That's totally okay. The name doesn't leak outside. – Bergi May 16 '17 at 00:39
  • 3
    http://kangax.github.io/nfe/ – Bergi May 16 '17 at 00:40
  • why would you do that? – vol7ron May 16 '17 at 00:42
  • 1
    In general you should do `function foo() { return 'Hello world'; }` - don't use function expressions when you don't need them. – Bergi May 16 '17 at 00:51
  • @vol7ron It seems easier and it's been what I've been doing. Just checking to see if there's something incorrect with it. – Hadoren May 16 '17 at 01:07
  • @Bergi Thanks for clearing this up! I just had an interview where the interviewer stated that function expressions are better. So I guess it's a matter of debate...? – Hadoren May 16 '17 at 01:08
  • @Hadoren Maybe "when you don't need them" is the debatable part, but I cannot imagine a reason why a function expression would be "better" in general - a declaration is simpler and gets hoisted. Did that interviewer gave any reasoning? – Bergi May 16 '17 at 01:14
  • @Bergi He gave me this link, which I haven't yet had the time to look at: https://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/ – Hadoren May 16 '17 at 01:18
  • 1
    @Bergi: There's a school of thought that hoisting is Bad because you can't reason about code linearly, is the only thing I can think of. `const foo = () => ...` is all the new rage I see often. – Amadan May 16 '17 at 01:20
  • 1
    @Amadan Yes, it looks like he doesn't like them because he is (or once was) confused on hoisting. In contrast I prefer the more declarative and concise function declarations, and try to avoid the *variable assignments* that require me to reason linearly. I guess it's because I favour (purely) functional programming, and despise the imperative mindset. (The article is oddly ambivalent - first it states "*programmers arrange their statements in a particular sequence for a reason*" and "*we are creating an object*" but then it argues that expressions are required for functional programming) – Bergi May 16 '17 at 01:35
  • @Bergi because functions should be scoped. I suppose it depends on the backend, but when you have an asset pipeline, aside from intentional hoisting, I don't know why I would use declarative functions – vol7ron May 16 '17 at 02:32
  • @Hadoren aside from some of the edge cases you'll find in the answers, there really is not too much wrong with it, but it's completely unnecessary. Good practice is to use namespaces and more explicit naming. The big disadvantage is that your convention is repetitive and would take more characters. Example: `Application.EmailInterface.refreshCurrentInboxAfterSend = function refreshCurrentInboxAfterSend(){ ... };` – vol7ron May 16 '17 at 03:11
  • @vol7ron Yes, when I want to make my function available to a scope, or part of a module, I *declare* them. When I want to pass them around as values, I use expressions of course. (Intentionally). – Bergi May 16 '17 at 03:20
  • @vol7ron not sure what you mean by "asset pipeline"? – Bergi May 16 '17 at 03:20
  • @Bergi various backends have their own MVC architecture. Something like (but not limited to) Rails use an asset pipeline, but in general it's the process of packing your assets (stylesheets, javascripts, etc) into on compressed file. The pipeline/ordering are important and name spacing is important. – vol7ron May 16 '17 at 03:21
  • @vol7ron Not sure what asset pipelines have to do with this. And even namespaces are declarative - I write ES6 modules with function declarations to export. – Bergi May 16 '17 at 03:26
  • @Bergi asset pipelines have the ability of condensing similarly named functions into the same file, overwriting an already defined function. There are ways around that, especially with ES6 and better code control, but when you work on a big project with many other coders or if you inherit something old and buggy, I'm sure you'll understand that the world isn't perfect and not everyone thinks the same. Namespaces are declarative, but the point being made was to primarily to avoid hoisting and re-declaration. – vol7ron May 16 '17 at 04:02
  • @vol7ron I understand that the world is not perfect, and that buggy (scope-ignoring) asset pipelines can force us to use function expressions. But that's not an argument to prefer one over the other. Nor is the possibility of redeclarations - those bugs happen when reassigning a variable as well, and we use linters to catch those mistakes. Regarding hoisting - I don't see why it's desirable to avoid that. – Bergi May 16 '17 at 04:18
  • Exactly - if linters are in place they are great. `let` variables are also great at avoiding collisions. The asset pipeline also has ways to address it. But it's not always easy when patching someone else's code. Also, there exists other implementation techniques that introduce the same problem; for instance, lazy loading webparts. Naming collisions are a lot less frequent if you namespace your function into a key of a global object, not to mention it allows reusing the same meaningful name for different components of the app (e.g., `App.Inbox.updateTable` and `App.Contacts.updateTable`) – vol7ron May 16 '17 at 12:08

2 Answers2

4

The main difference is that in your first example, when inside the function, foo is a shadowed variable, and doesn't refer to the global foo. For example:

var foo = function foo() {
 foo = 1;
}
foo();
console.log(typeof(foo));

outputs 'function' because we have only redefined foo inside the function.

However if you defined the function the second way, foo refers to the global object:

var foo = function baz() {
 foo = 1;
}
foo();
console.log(typeof(foo));

And this will output 'number' since we have modified the global object.

david25272
  • 976
  • 6
  • 12
2

It depends on what you want. The inner name is local to the function; the outer name is in the outer scope, and is captured by the closure. Thus, if you name your functions differently, you can reassign the outer reference from the inside:

var foo = function baz() {
  return foo;
};
var quux = foo;
foo = 17;
quux(); // => 17

This is not possible when you name the functions differently the same, since the outer variable will not be accessible, shadowed by the inner one. If you want this scenario, then you will need to name them differently. However, I would not say there is any problem in naming the functions either way.

EDIT: Unless you need to support end-of-lifed browsers. Then beware dragons.

Amadan
  • 191,408
  • 23
  • 240
  • 301