My oppinion is that Javascript is not bad as a scripting language, but we are abusing it and twisting it beyond its original purpose. The main issue is actually that Javascript is too flexible. Untyped code has an habit of hiding mistakes in hard-to-debug ways. But once you add types to Javascript, it's not Javascript anymore.
A couple of points. First, the argument you're making is for static -- i.e. compile-time -- semantic checking. I was hearing the very same argument thirty years ago among people who advocated Pascal over C; on paper it's sound, but decades of practice have convinced me that static checking, while probably helpful, is not as efficacious as it seems like it should be. After all, you *still* run into mishandled exceptions in Java, and many Java programmers do an end-run around around much of the compile-time restrictions by using runtime exceptions; in fact wrapping low-level checked exceptions in runtime exceptions are a feature of some frameworks. It's hard to get programmers out of the mindset that a core dump, or an unhandled exception, is some kind of serious calamity. You don't want them in production code of course, but they're far less calamitous than continuing processing with bad data. The place to catch those problems is in testing.
As for the other kind of compile-time problem, handing an object to a routine that expects an interface the object does not support, what scripting languages like Python have shown is that it's not that big of a problem. Large, sophisticated systems programs have been written in Python, which lacks precisely this kind of checking.
I'm very comfortable with Java, but there is something about the design of the language and its core libraries which encourages over-engineering. For example, itis possible to do asynchronous http handling in Java EE, like you do in Node.js, but in Node.js you simply create a non-blocking event handler for every case you want to handle. In Java EE, if memory serves, you create a ServletContextListener with a Queue member, and put that member in the servlet context. Then in your servlet you create an AsyncContext and put it in the Queue. None of this is as complicated as it sounds, but it only covers the case when you want to keep a socket open to the client, like when you're doing Comet. If you want to conserve resources by serving your I/O requests using polling rather than blocking threads (the primary Node.js use case), you've got to use java.nio, which can get complicated.
On paper, designing programs in Java is a cinch with Java's rich OO features. Any reasonably competent programmer can quickly learn enough to demonstrate facility with the *features* of the language. In practice as a programs required feature set grows, the number of classes and interfaces explodes, unless you are a very talented software designer.
While it's true most Javascript programmers write simple onload and onclick handlers, this doesn't make Javascript a toy language. While its object-oriented features are relatively primitive, its functional programming features are quite sophisticated, and this turns out to be just the thing when you are writing sophisticated event handlers, as in Node.js. Expert Javascript programming is about implementing higher order functions which return closures, something that current versions of Java can't do (Java SE8 is getting lambda expressions, so this might change). Functional programming is all about getting a single transformation of inputs to outputs correct; it feels *contained* -- that is to say you don't need to think a lot about the context in which your code runs. Programming in Java is often an exercise in information overload, as it typically involves mastering the nuances of multiple complex libraries and frameworks so you can fit them together properly.