Hacker News new | past | comments | ask | show | jobs | submit login
Javascript patterns (shichuan.github.io)
210 points by ekpyrotic on Jan 1, 2014 | hide | past | favorite | 45 comments



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/...).

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.


Using 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?


> 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.


Personally 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.


Massive use of unclear 'it' reference in this text. Funny how it reminds me js..


Sorry - accidentally downvoted (this is what happens when I HN on an iPad). But thank you - agreed. I couldn't figure out what the parent comment was actually referring to either!


I 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


We 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) {
      // ...
    }


that'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...


Actually, 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.


Not 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.


And 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.


Yes, 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

(Please correct me if I'm wrong, I wasn't aware for example that function declarations were treated as variables, redefinable etc)


The 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.


I 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.


I would rather have code that always works than have it be all pretty and semicolonless.


It 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.


Sure, 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 :)


Everywhere 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);
    })()


Rather than putting a semi-colon after every function declaration, I would suggest the Lua convention -- put the semi-colon at the beginning of the line that could cause issues. An empty statement is valid is JavaScript, so this should work in all cases I can think of:

    ;(function(){
        var foo = 1;
        console.log(foo);
    })()
In Lua, whitespace is very nearly all the same. For example:

    a = 1 b = 2

    c
    =
    3
is valid Lua code. However, lines that start with parens have the same ambiguity:

    a = something
    (expression)(arguments)

    --Could be:
    a = something(expression)(arguments);
    --Also could be:
    a = something;
    (expression)(arguments);
The parser will complain about the ambiguity. If the intention is to have 1 statement, they should probably be on the same line, otherwise the convention is to put the semi-colon at the beginning of the second line:

    a = something
    ;(expression)(arguments)


That would be a change of paradigm because no JavaScript standard -or library- does that. Plus it feels wrong because the spoken language equivalent would be something like:

    The quick brown fox jumps over the lazy dog
    . The fox didn't say anything that day
    , because in all honesty what does the fox say
    ?


I 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


Yeah, that's correct; no discussion on that, and no linter that I have used asks you to put a semicolon in such case.


This UI is pretty terrible on somewhat small screens. Here's what it looks like when sized to 1024x768:

http://i.imgur.com/X78sHxx.png

Note that the links behind the blue translucent rectangle are unclickable.


Grey on grey is usually a bad usability move if you expect people to read your content.

(For FF use nosquint: https://addons.mozilla.org/en-US/firefox/addon/nosquint/ and Chrom* use high contrast: https://chrome.google.com/webstore/detail/high-contrast/)

I don't know about others, but there's really no chance I'd be physically able to read those words without things like this. My eyes blur and I feel a physical strain on them - like I'm trying to do some type of eyeball aerobics.

I literally can't read more than about a sentence without having to look away a few moments and then return.


I can read grey on grey just fine, even on my old laptop with its terrible color and worse grey resolution. Moreover, that site isn't grey on grey, it's grey on light cream ( https://en.wikipedia.org/wiki/Cream_(colour) ).

If you haven't already, go to a doctor and get your eyes checked out. What you're describing is not typical.


I've gotten my eyes checked regularly for 20 years. Nothing medically troubling to report. I'm slightly color blind and wear glasses.

I really want to read the content and the substance - but the modern low contrast design has made it difficult for me.

Some particularly aggressive sites I print out to read on physical paper.

I was able to easily read content for my first 18 or so years on the web until the trend started.


Considered adding a Github Issue for this? https://github.com/shichuan/javascript-patterns/issues

(or do you mind if I add one for you?)


Feel free to add one for me. Link to that imgur picture if you care to. Thanks! :)


Or even better. Consider to send a pull request too.


Are you really going to write "var getData = function getDataF () { };"

instead of "function getData() {}"

for pseudo benefits like

* 1. Makes it easier to understand "functions as an object". * 2. It enforces good semicolon habits. * 3. Doesn't have much of the baggage traditionally associated with functions and scope.

?


Agreed, and what exactly is the baggage traditionally associated with functions and scope that you avoid by using a function expression?


Previous discussion: https://news.ycombinator.com/item?id=5392984

This source has some problems.


It's not bad, but you barely scratch the surface. Here's one: http://stackoverflow.com/questions/12973706/javascript-colle...


Aren't these considered "idioms"?


Why call eval like this? (1, eval)('this');


I'm pretty sure it's due to http://es5.github.io/#x10.4.2 section 1A.

Calling eval like that causes it to be an indirect eval call which results in it being executed in the global context.


I was wondering too. There is a nice answer at SO that expands on what dcherman said: http://stackoverflow.com/a/9107367



Very cool. I'd recommend adding something about callbacks defined in loops, ie:

  for (var i = 0; i < 10; i++) {
    $('.thing').click(function() {
      alert(i);
    });
  }


one page PDF would be great


Good.


these patterns are cute. I especially like the one where for loops are described, as if the variable declaration practices are important.


And the recommendation to avoid i++ due to "excessive trickiness?" followed by a recommendation to use while(i--) with i initialized somewhere else as a for loop substitute? What the heck? That sounds terrible.


I thought I might learn something until I saw that exact comment, then decided it was safer to stop reading immediately.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: