for (x=0;x<variable.length;x++) {
// blah
}
I wasn't declaring the x variable, so it was using the x part of the x/y positioning of the UI container. Woops.edit: initialise/declare brain freeze
It also doesn't happen in languages where you must say self.x to access a property/field called x.
Do you mean s/initialising/declaring/, or am I just really confused?
Between that, and using -Werror to ensure that warnings never get ignored, I find C99-style declare-anywhere quite useful. It helps me keep the scope of variables limited to the places that need them.
That said, I rarely use C99's ability to declare a variable in the middle of a block. I primarily use the C99 feature of declaring variables as part of a loop.
Scheme got it right sometime in the 1970's.
(let ((x initial-value)) ; binding
(set! x new-value)) ; assignment
Or in infix syntax (Dylan): let x = initial-value ;
x := new-value ;https://en.wikipedia.org/wiki/ALGOL_58#ALGOL_58.27s_influenc...
(define x 42) ;; global
(let ((x 42)) ;; local, only defined inside the enclosing parenthesis.
(+ x x)) sum = 0
for v in someList:
smu += v
print sum # prints 0, doh! /* "use strict" */
construct would have caught this mistake (just like it would have done for me in perl code)? Seems to me that some such feature is absolutely and utterly necessary in any environment where you're doing a lot of closure creation .. $ node
> "use strict"; initial = "foo";
ReferenceError: initial is not defined
at repl:1:17
at Interface.<anonymous> (repl.js:168:22)
... ((x)-> x = 2; arguments[0] is 1)(1) # true
((x)-> arguments[0] = 2; x is 1)(1) # true
Any developer enabling strict mode should be testing heavily in browsers that actually support the feature. There have been a few high-profile meltdowns already (on BoA, Intel, eBay, etc.) due to devs not understanding the feature, or concatenating a `"use strict"` script (not wrapped in an IIFE) with non-strict code.JSLint doesn't help matters, as it errors unless `"use strict"` is declared (unless run with the "sloppy" option -- something I'm sure devs love). The absolute worst scenario is for a browser to implement some kind of heuristics-based approach to respect the `"use strict"` prologue; what Brendan Eich calls a "strict quirks mode".
All that said, I'm a big fan of `strict`. It might be a good idea to use it in production, and strip the directives out when deploying.
Maybe Node should be strict by default? I can understand it not being the case for browsers that need to support legacy code, but for Node it doesn't really need to care about that.
I think, if anything, requiring it to be in a comment would have been less odd; at least comments imply a sense of "meta"-ness.
use v5.12;
in perl code enables strict, at least.If so, that is an insane design.
So you just don't use them and everything's good. Which brings you to the real problem: JavaScript makes it distressingly easy to use a global variable where you meant to use a local one.
> Global variables are basically instance variables on the
> process itself.
Not in JS. See my response to ww520 below.All languages used in web server that don't spawn a new process for each request have the same feature.
It's trivial to have a single-process JS application with multiple JS global objects in it. Case in point: any web browser (and renderer processes don't change this: every iframe on a page has a separate global object).
It's just that Node _chose_ to reuse the same global object for multiple requests instead of using a clean execution environment.
I have to agree with jessedhillon: that sounds insane.
Happily, this is all changing--in the long run, future versions of JavaScript should polish away these issues while maintaining the fundamentally sound core of the language. In the short run "use strict" lets you realize some of the upcoming improvements in otherwise legacy code.
In my opinion it wouldn't be quite as scary if js didn't make it so damn simple to accidentally define a global variable (like in this instance, where it was never actually declared in globally-scoped code).
Note that nowadays going to Coffeescript from Javascript is quite easy: http://js2coffee.org/
I imagine that must be a headache for non-native speakers for whom "l" and "r" are indistinguishable. Imagine coding in a language developed in China with keywords "mā" and "mǎ"… most English-speaking developers would never remember which one to use because they can't distinguish between the two verbally.
In this case as long as OP was not using the variable in an enclosing scope, it would have been made local to the function.
jshint would have caught it. You need to run jshint on your code or you will get silly errors like this. Simple.
I've done some moderately complicated jQuery development with it, as well as some node.js stuff for fun, and have had no issues in either case.
Given that you said that, and that the problem you hit is a common pitfall for Javascript developers (especially if you're not very seasoned with the language), I'd strongly recommend picking up a copy of Douglas Crockford's Javascript: The Good Parts. Not only does he inform readers of this particular gotcha, but he also elaborates on Javascript best practices and tools that others are bringing up in their comments.
"use strict" is catching a lot more errors, though frustratingly enough, not until runtime.
Of course, there is a way to find his particular bug (JSLint, "use strict") that he didn't know about. Still, the point about debugging being a nightmare is absolutely true.
1. Did a major last minute code change
2. Did not run jslint
3. Did not use strict mode
4. Did not run load tests
5. Did not use an editor that catches scoping problemsI always thought JS's global-variables-by-default schtick was it's worst crime, but I've never seen such terrible consequences for it up close before.
Note to self: before products get to forbes, do some load testing. Even if I am using node, with its magic event loop of invulnerability.
Get a better editor, and JSHint, and "use strict";
Implicitly declared globals can be statically checked, there is no reason not to.
> (function () { internalvariablewithnovardelcaration = "string"; })();
> internalvariablewithnovardeclaration
ReferenceError: internalvariablewithnovardeclaration is not defined var a = "foo",
b = "bar",
c = "baz";
All it takes is one missed comma and all of a sudden you've turned a lot of your variables global. var
a = "foo"
, b = "bar"
, c = "baz"
; var a = "foo";
var b = "bar";
var c = "baz";https://github.com/douglascrockford/JSLint
Setup and how you use it is more important than just using it. I run it in three places:
1) In my IDE (bound in vim on save, same in TextMate)
2) As a Git checking hook
3) In deployment / build scripts
Turn all the warnings way up and it always catches redeclaration bugs.
Set it up once and don't allow any code to get checked in or deployed with even a single warning present.
I also pass all Javascript through the Google Closure Compiler, but with the lowest compilation setting, because it is very good at picking up small errors as well.
(if anybody is interested in how to set this all up I can publish my configs and scripts)
This technique totally went to shit when I came across one of our scripts that gratuitously used `apply()` all over the place.
The other common error is array iteration, and I've not quite understood why iterating through one array in the same scope as where it was created works fine, but passing it to another function and performing the exact same routine also goes through the prototype methods after the elements.
Of course, particularly with the var mistake, you'd never really understand the magnitude of it until you attempted to use JS on the server side. This post has, quite thankfully, likely exposed a bug in my own code I couldn't quite understand a while ago. :)
> also goes through the prototype methods after the
> elements.
That shouldn't be happening. If you see it happening, your JS implementation is just buggy.In fact, this testcase:
<script>
window.onload = function() {
var arr = window[0].arr;
for (var i in arr) { alert(i); }
}
</script>
<iframe src="data:text/html,<script>arr=[1];</script>">
</iframe>
alerts only "0" in WebKit+JSC, Gecko, Presto. Chrome has some sort of bizarre security policy here that keeps the script from working, so no idea what WebKit+V8 does.http://code.google.com/p/js2-mode/
Mind you it won't catch globals declared in chained assignments
eg
var x = y = 0; // y is defined globallySorry, that's incorrect. If you cannot fully simulate your environment for purposes of validation, you're not covering your bases.
That sounds like a necessary idea if you're building real-time embedded systems for aircraft operation or something, but for a lot of applications, developing a test harness to that standard would take considerably more time and effort than building out the actual product.
But, if the environment is such that it is easy to manifest this condition, I would argue that such a test setup should definitely be considered, given the potential for failure.
It's easy to forget to go "var me = this;" and use me instead of this inside of closures. But how would JSLint or JSHint know you made a mistake and didn't actually mean for "this" to be bound dynamically?
It would be able to find out if you used an uninitialized me, _this, that though and complain. Using self as the this alias is really dangerous as it leaks to window.self so avoid it like the plague.
Hell yea, the valley's full of smart kids who don't know what the hell they're doing with JavaScript and thinks they're one hell of a hacker when they discovered that they can do shit with a few lines of JS and trumpeting on the how great NodeJS or whatever the greatest and newest shiny framework out there.
If you don't really understand scoping in JS, please don't use node and hit yourself on your foot and then blog the cool shit out of it.
I'm just saying, no hard feelings. :)
I'm sure this guy "really understand[s] scoping in JS", but he still got bitten. The blog post isn't frivolous, its point is that javascript is a minefield. Which is an important lesson. Use JSLint or "use strict" or pay the price.
Perfect example of a developer hating JS just because he/she didn't bother to learn it first and got burned.
Running your code through JSLint (or something similar) would have been helpful here. Performing this check before committing code is considered a best practice and it's really easy to setup a pre-commit hook in most version control systems. JSLint has a Node.js mode. It would have said something like "variable used before being defined". http://www.jslint.com/
Of course it doesn't prove that your code is bug-free, but if you have concurrency issues, this will help shake them out. I've used this many times to reveal problems with DB connection pooling, concurrency issues, and excessive session size.
After everyone got some sleep, we realized that Cupcake didn't have multi-res support; and later phones quietly re-scaled everything to compensate. Compile to Eclair and well, things were a bit out of proportion.
Also, C++ can also take down your site with a single character typo that the compiler may not catch, so even static typing can't save against everything! (I actually like C++, too, so no hate there)
> (function () { internalvariablewithnovardelcaration = "string"; })();
> internalvariablewithnovardeclaration
ReferenceError: internalvariablewithnovardeclaration is not defined
at [object Context]:1:1
at Interface.<anonymous> (repl.js:179:22)
at Interface.emit (events.js:64:17)
at Interface._onLine (readline.js:153:10)
at Interface._line (readline.js:408:8)
at Interface._ttyWrite (readline.js:585:14)
at ReadStream.<anonymous> (readline.js:73:12)
at ReadStream.emit (events.js:81:20)
at ReadStream._emitKey (tty_posix.js:307:10)
at ReadStream.onData (tty_posix.js:70:12)
> externalwithnovar = 1;
1
> (function () { externalwithnovar = "string"; })();
> externalwithnovar
'string'
You're all wrong. There is no 'global by default for javascript. This guy clobbered a variable called initial outside of the scope of the callback.Turns out I forgot to use 'var' in a library function, and the tests were running concurrently, and so one clobbered the other. It was not fun.
Been using JSHint ever since.
I realised this after about 10 minutes but already multiple copies of the same emails (20/user) was send to more than 10k users!...The end result was annoyed users unsubscribing in droves and possibly a hit in our email reputation. This is all because a stupid array variable was not initialised :)
But the problem is a bit more meta than Javascript scoping. Something would have happened no matter your environment. It's always extremely risky to make huge changes just before a launch day. Stuff always breaks. Sounds like you did a good job of communicating with your customers. Ironically, they will probably be better customers than they would have been if you didn't have any problems. Overcoming adversity brings people together.
Add some concurrency to your test suite. If you have any system level or black box tests, you can often just run the same test multiple times in parallel with different threads. No new tests required. Note that this may (probably will) also uncover other previously inconceivable concurrency issues.
This is one of the reasons I like shared-nothing architecture where the only way to share or persist something is via explicit caching or database.
Sorry to hear about your tragedy, hope it doesn't cause too many issues in the long run.
bullshit.
jslint or clojure compiler would have found this error at "compile time."
coffeescript would have made it a non-issue.
I might just be painfully slow, but the implementation he described didn't sound anything remotely simple to me (though I believe it really is beautiful).
It's these small things to remember that can save a lot of debugging time down the road. Will remember to "use strict" when I use nodeJS again.