Javascript patterns
shichuan.github.io
shichuan.github.io
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.
This seems more of enforcing one's personal opinion than anything else, to me.
(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.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 :)
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);
})() 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 ;(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) 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
?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.
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)
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.
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
function explicitize (fn) {
return function () {
return fn.apply(null, p(arguments, this));
}
}
Now you can write: foo.prototype.bar = explicitize(function (myself, something, etc) {
// ...
}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?
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.
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.
http://i.imgur.com/X78sHxx.png
Note that the links behind the blue translucent rectangle are unclickable.
(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.
If you haven't already, go to a doctor and get your eyes checked out. What you're describing is not typical.
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.
(or do you mind if I add one for you?)
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.
?
This source has some problems.
Calling eval like that causes it to be an indirect eval call which results in it being executed in the global context.
for (var i = 0; i < 10; i++) {
$('.thing').click(function() {
alert(i);
});
}