4 ms·
Regarding function declarations, it considers function declarations to be an antipattern and advises you to instead use function expressions at all times (https
by rnd33 13y ago
Regarding function declarations, it considers function declarations to be an antipattern and advises you to instead use function expressions at all times (https://github.com/shichuan/javascript-patterns/blob/master/general-patterns/function-declarations.html https://github.com/shichuan/javascript-patterns/blob/master/...).
I'm really skeptical about this. Using function expressions has some quite subtle consequences (hoisting, accidental/unintended closures) which you automatically avoid when using function declarations. Function declarations are also very useful since they allow you to use the function before it is defined (in the code), which mean you can read the code (and use the function name as an abstraction) without having to scroll past all function implementations.
To expand a bit on the accidental closures part, a variable bound by a closure is an implicit dependency of that function. By breaking function expressions out as function declarations you can be more explicit about what kind of input that piece of code needs from the outside. I just think overuse (nesting etc.) of closures can lead to some quite messy code, it gets harder to reason about when a variable was actually bound and to what.
- fbgv 13y agoI also doubt its enforcing of good semicolon habits, considering that semicolons are not needed after many a statement in C-like languages, including Javascript. This seems more of enforcing one's personal opinion than anything else, to me.
- ivanca 13y agoIt does bite you if you are not careful, for example: (function(){ var foo = 1; console.log(foo); })() // Semicolon missing! (function(){ var bar = 1; console.log(bar); })() It's ambigous for the parser so this will throw a TypeError: "undefined is not a function". So overall I don't think is a stylistic choose but a safety one.
- rnd33 13y agoSure, but the solution should not be "semicolon everywhere" but rather "semicolon where necessary", where necessary also includes places that can produce ambiguities depending on the code directly following (or after minification). Omitting a semicolon after a function declaration will never produce ambiguous code. What I'm still arguing for is that "enforcing semicolon habits" is not a compelling argument for using function expressions over function declarations. (Perhaps you're talking about semicolons in general, which is something I would rather not get into :)
- ivanca 13y agoEverywhere where it _can_ create an ambiguity (regardless if it does or not); having to look at previous unrelated code to see if you need or not a semicolon is inefficient; better put it everywhere where any adjacent code can create ambiguity. And function declarations can create ambiguous code, because functions are first class citizens on JavaScript it means code like this is valid: var foo = function (bar){ console.log(bar); } but for the lacking semicolon it throws an error if it is followed by: (function(){ var foo = 1; console.log(foo); })()
- rnd33 13y agoI think I've been unclear. When I say function definition I'm referring to this: function foo() { console.log('bar'); } And when referring to a function expression I'm talking about this: var foo = function () { console.log('bar'); }; The latter needs to be terminated with a semicolon to prevent ambiguity (in some situations), while the former does not. This for example is valid JavaScript: function foo() { console.log('bar'); }foo=5
- ivanca 13y agoYeah, that's correct; no discussion on that, and no linter that I have used asks you to put a semicolon in such case.
- deleted 13y ago[deleted]
- woah 13y agoI would rather have code that always works than have it be all pretty and semicolonless.
- arnorhs 13y agoActually, the reason you can use the function at a place in code before it's defined is actually because of variable hoisting. The name of the function is hoisted to the beginning of the scope in which it's defined. (this may be what you mean, but it was unclear from the comment) But I agree that I don't think doing var func = function(){}; is in any way better than a normal function declaration. Actually, I think it's a bit ridiculous to even have one recommended over the other.
- rnd33 13y agoYes, a function definition is hoisted too but its implementation is always hoisted together with its declaration, preventing some bugs caused by variable hoisting. I made an example here: http://pastebin.com/2nS9EDPb http://pastebin.com/2nS9EDPb (Please correct me if I'm wrong, I wasn't aware for example that function declarations were treated as variables, redefinable etc)
- jbnicolai 13y agoNot just the name, the entire function declaration is hoisted. That's the difference between: function () { ... var a = function () {}; } which gets hoisted to: function () { var a; ... a = function () {}; } and: function () { ... function a () { }; } Which gets hoisted in it's entirety to: function () { function a () { }; ... } Which will cause difference behaviour between the two when calling a in the ... section, working in the latter case but undefined in the first. I don't think the grandparent was unclear about this in his post though.
- Zecc 13y agoAnd as a bonus, the latter gets its name set. Of course you could also name it in the function expression, but then you'd just be repeating yourself.
- jaredmcateer 13y agoThe main argument for a function expression assignment is that it behaves like everything else where as the function declaration does some magic. As long as you and all your team members are aware of said magic it's probably fine but personally I would side on consistency.
- tracker1 13y agoI happen to rely on function hoisting as well.. which each file being a module, I tend to define the module at the top, and have declarations below. I also tend to prefer methods that accept an object parameter as opposed to using "this" and will often put wrappers on objects such as... Foo.prototype.bar = function(){ Foo.bar.apply(null, p(arguments, this)); } with p being an alias to Array.prototype.shift.apply, put the second arg to the top of the former as an array... it's generally wrapped in a utility function... With that in place my entire prototype's functions are just pass through to static Foo.fn .. in general this makes it easier to test modules that are instance objects. Though prefer to have utility modules over modules that expose an object constructor... Exception being Models, which inherit from EventEmitter2
- raganwald 13y agoWe have a special on combinators this evening. Why not try a side order? function explicitize (fn) { return function () { return fn.apply(null, p(arguments, this)); } } Now you can write: foo.prototype.bar = explicitize(function (myself, something, etc) { // ... }
- tracker1 13y agothat's pretty much what I meant by wrapped in a utility function... generally... something akin to explitize(ConstructorName, 'methodname') .. so that the passthrough call(s) can be shimmed/mocked for testing...
- raganwald 13y agoUsing function expressions has some quite subtle consequences (hoisting, accidental/unintended closures) which you automatically avoid when using function declarations. Obviously, there is the issue of hoisting the function's expression as well as its name when you use a declaration, versus only hoisting the variable when you use an expression. Are there any other subtle hoisting consequences to consider? With respect to accidental/unintended closures, can you elaborate on this? Perhaps provide an example showing why a function assigned as an expression creates an accidental closure but a function that is declared does not? You say: A variable bound by a closure is an implicit dependency of that function. By breaking function expressions out as function declarations you can be more explicit about what kind of input that piece of code needs from the outside. I admit I don't understand at all how a function declaration changes the behaviour of its free variables. What is there about a function declaration that provides more control over its dependencies on its enclosing environment?
- rnd33 13y ago> Are there any other subtle hoisting consequences to consider? I can't think of any, other than consequences from the hoisting such as making the order of the variable declarations important (whereas it's not important if they were function declarations). > I admit I don't understand at all how a function declaration changes the behaviour of its free variables. What is there about a function declaration that provides more control over its dependencies on its enclosing environment? You are correct, it doesn't. I realise now that in my head I was not strictly comparing function declarations to function expressions, but rather top-level function declarations (like in C) versus function expressions defined at some inner scope. Using function declarations at any other scope than the top-level is something I would consider an anti-pattern (if I had to use that word), since it's very confusing to programmers from other C-like languages. > With respect to accidental/unintended closures, can you elaborate on this? Perhaps provide an example showing why a function assigned as an expression creates an accidental closure but a function that is declared does not? So, as I said above I was referring to function expressions versus top-level function declarations, which probably makes this question obsolete. I should perhaps have said unnecessary closures, since if you capture 10 variables but only use one then perhaps you should rethink your design. Closures are super useful but they tend be abused (yay access to everything!) which leads to bad design (low separation of concerns and overall spaghetti code). I suspect from your questions you already know all of this. I should have been more clear that I was not talking about any semantic differences between function declarations and function expresses, since you are correct in that there are none, but rather the design choice of using a function expression (and thereby capturing the variables in scope) versus breaking it out as a top-level function declaration thus making it necessary to explicitly state all input as arguments.
- mattmanser 13y agoPersonally whenever I see a javascript programmer doing this with simple functions, I think it shows a pretty fundamental misunderstanding of programming to me. The guy even hints at the misunderstanding in his code with the comment 'Makes it easier to understand "functions as an object"'. So what, most other languages have this now, but you don't see them taking one of the fundamental building blocking of programs and code encapsulation, simple function declaration, and bunging it in the middle of another method. It's much clearer using the style for closures only so you're explicitly making it clear 'hey look, I'm making a closure people!!'. It's a style that's practically begging for you to write heavily coupled code and is anti-code reuse. It's a terrible habit, it was all started by Crockford's the good parts, a style he just happened to be using at the time as far as I can tell, and even he doesn't even do it any more. It also makes your code harder to read as you can't just move the function declarations wherever you want, just in case. IMO you should never assign a function to a variable unless you are going to use it as a closure or actually use it like a variable and potentially over-ride it later in your code. To me it's a massive code smell when I see simple functions assigned to variables.