If you have used Java as a programmer, then you have likely tried to make comparisons to JavaScript. As comfortable as it is to stick to what you know, it can get you into trouble quickly. After thinking through a problem carefully, you begin programming, only to realize what you want to do is impossible. I thought I would touch on a few of these limitations within JavaScript with the hope that you are spared the same frustrations that I endured.
JavaScript’s global Object has no hashCode(), and therefore, no equals()
Not to be confused with a Java Hashtable or HashMap, the JavaScript “hash”, also known as object literal notation, is only a style for representing an object’s methods and properties. These object “members” are represented as key/value pairs, which is where the confusion with the Java Map collection comes into play. This is the only real resemblance between the two.
The occasion often arises when a programmer would like to treat a JavaScript hash like a Java HashMap, and prevent duplicate keys from being inserted. However, the limitation in JavaScript is that the global Object super constructor has no hashCode() because objects in JavaScript do not contain hash codes. The equals() method in Java uses these hash codes to determine equality between objects, which is what in turn helps to prevent duplicate keys.
The common response to the problem is to loop through all of the keys for one or more JavaScript object literals, and to compare them to a value to determine if a duplicate exists. This is an expensive operation, and it is why the Java hash code is crucial to performance during lookups for a specific key. Large JavaScript object literals that are used as data structures will slow an application down if you constantly loop over the key/value pairs.
Namespaces are in-memory objects, and not references to a package
It makes sense that since I just mentioned the object literal, that I would also talk about namespaces (or packages). The object literal (dot) notation is the most common way in which namespaces are replicated in JavaScript. Here is a typical example of what a namespace might look like:
var DBUG = {
ajax : {
get : function() {},
post : function() {}
},
animation : {},
utilities : {}
};
DBUG.ajax.get();
Just like Java, the implementation of a namespace in JavaScript can help provide varying degrees of access control to the members on an object. However, these implementations vary widely, and do not provide the level of control that classical-inheritance, packages and access control keywords do in Java. More than anything, in JavaScript a namespace is the best way to keep variable and function names from conflicting in the global scope of the page.
You should take caution with this approach because when you use an object literal, as well as nested object literals, you are creating new objects during script execution. The JavaScript hash is actually a shortcut for using the new keyword when creating an object. The example above technically has six constructors, but also has four new objects created. Compare that to the following, which only has five constructors, and no new objects created.
function ajax() {
this.get = function() {}
this.post = function() {}
}
function animation() {}
function utilities() {}
You might be thinking to yourself, hey, aren’t functions just objects in JavaScript, so what’s the big deal anyway? I’m going to address that in a future post, and in this example there is minimal impact. As John Resig pointed out, there is a lot more to performance than just JavaScript. However, taking into account that the object literal is often used as an abstract model for storing data “locally” in AJAX applications, performance can dip. I decided to run an informal test on the two scripts above to be sure this was a correct assumption.
TEST 1:
<div id="results"></div>
<script language="javascript" type="text/javascript">
var d = new Date().getTime();
for ( i = 0; i < 100000; i++ ) {
var DBUG = {
ajax : {
get : function() {},
post : function() {}
},
animation : {},
utilities : {}
};
}
document.getElementById("results").innerHTML = new Date().getTime() - d;
</script>
TEST 1 RESULTS:
Firefox 2.0.0.1.8 ( 656ms – 906ms )
Internet Explorer 7.0.5730.11 ( 906ms – 922ms )
TEST 2:
<div id="results"></div>
<script language="javascript" type="text/javascript">
var d = new Date().getTime();
for ( i = 0; i < 100000; i++ ) {
function ajax() {
this.get = function() {}
this.post = function() {}
}
function animation() {}
function utilities() {}
}
document.getElementById("results").innerHTML = new Date().getTime() - d;
</script>
TEST 2 RESULTS:
Firefox 2.0.0.1.8 ( 219ms – 250ms )
Internet Explorer 7.0.5730.11 ( 15ms – 32ms )
I was primarily surprised to see such a drastic increase in IE performance, especially on such a small namespace. Although 100,000 iterations does simulate a high load, you can see that object instantiation comes at a price. You do not need to retool all of your JavaScript, but you should think twice about how you will structure your Web applications on future projects.
Threading is not supported, so concurrency is impossible
Threads, multithreading, or threading, allows a programmer to run concurrent processes. How threads work is not only dependent on the language, but also on the operating system. A dual-core operating system can use each core to manage the threads, while a single-core OS will use Time-division multiplexing. Threads can starve each other, or run into a deadlock — this is an advanced topic because you can use up system resources, and therefore, easily crash an entire application.
In case you did not already know, JavaScript execution runs as a single thread. As a matter of fact, the execution of JavaScript is only one component in a long thread of executions, which includes CSS, the DOM and a rendering engine. Google Chrome is a bit different, in that every instance of the browser (or tab) runs a thread, which is why developers make the mistake of saying Chrome is multithreaded. The truth is that Chrome runs each thread in a sandbox. That means one JavaScript thread is unable to communicate with the other.
Many attempts have been made to emulate threading in JavaScript through the use of timers (setTimeout() and setInterval()), mostly for AJAX and animations. Adobe Flash contains keyframes on separate timelines that run animations concurrently, and programmers have tried to use JavaScript for this purpose without real success. A few have pointed out that the accuracy of timers is not to be trusted as well, and the entire solution is usually a complicated and unreliable hack. It is better to accept the simple fact that JavaScript does not support threads.
[...] The JavaScript language limitations that every programmer should learn – Brian Reindel talks about some of the key limitations of Javacript that a developer from another platform may take for granted. [...]
[...] The JavaScript Language Limitations That Every Programmer Should Learn (Brian Reindel) [...]
And the classic, there is no synchronous sleep() or wait() method in JavaScript. Most attempts at creating them result in hammering the CPU in wasted cycles to eat up time.
How long have we been telling people in comp.lang.javascript that:
javascript !== java
It was all a waste of time me thinks… the limitation is only yourself.
I personally consider setTimeout to be simulated threading. It /does/ offer concurrent operations in JavaScript, while it isn’t spectacular, it exists.
Not that threading is really needed, I mean after all, JavaScript is just that, a light scripting language for the client side. It’s not meant to be much more.
Just to be more precise ;)
for 2 object instances in Java o1 and o2:
o1.equals(o2) implies o1.hashCode()==o2.hashCode()
but the other direction is, in general, not true.
So equals() has nothing to do with hashCode() as you state (but hashCode() depends on equals())…
And i definitively agree with Marc.
The ‘language’ attribute is depecrated. In fact, there’s little point in adding the ‘type’ attribute either (browsers default to JavaScript / jScript for IE) unless you really need your HTML to validate.
Java is severely limited because it doesn’t have gwbasic’esque line numbers and the handy REN and GOTO command. That used to so useful for control flow!
Java is also severely limited because it doesn’t offer any control on the on-processor memory register, like assembly does.
Java is also severely limited because it doesn’t let the programmer address memory registers directly, like C does.
Or maybe I am comparing apples to oranges, not understanding the uses/benefits of Java, not respecting the principal premise behind the language design and it’s abstractions, and talking out of my a**?
The reason that the internet explorer version is so much faster on test 2 is that it only defines the functions once. Try the following:
if (0) {
function f() { alert('alert'); }
}
f();
@christian
You are right. Not making a call to the function statement does speed things up a bit. However, in the case of any application the thought is that there is no reason for object instantiation unless you really need it. How much performance you gain in the second approach will really depend on the logic around your functions. Thanks for taking the time to comment!
The common response to the problem is to loop through all of the keys for one or more JavaScript object literals
Maybe I misunderstood the problem but you can use the in operator for that:
var obj={foo:0}
‘foo’ in obj // -> true
@Carl
Unfortunately, that only works on one level.
var obj = {
foo1 : {
foo2 : null
}
};
“foo1″ in obj //-> true
“foo2″ in obj //-> false
This is why looping is necessary.