Biz & IT —

JavaScript has problems. Do we need Dart to solve them?

JavaScript, the linchpin of scripted websites, is not a perfect programming …

JavaScript has problems. Do we need Dart to solve them?
Photograph by Aurich Lawson

Google hopes to upset JavaScript's dominance by introducing a new language, Dart. Dart is designed to be simpler, more familiar, and faster than JavaScript, and Google one day wants to see it everywhere: in the browser, on the server, and maybe even on the smartphone. Those are big ambitions, but before we take a look at Dart and at Google's plans for it, it's worth taking a closer look at JavaScript itself. Why exactly doesn't Google like it?

The oddball

JavaScript is a funny little language. Languages which buck trends rarely flourish, and though JavaScript mostly has the syntax and structure of a conventional, C-family programming language, the way it actually works is quite unusual. It's an object-oriented language, but instead of using classes, it has only objects and prototypes. (Classes are cookie cutters that can be used to stamp out a whole set of objects that all have the same behavior. To create multiple objects with identical behavior in JavaScript and other prototype-based languages, you take an object with the behavior you want and clone it.)

Every other object-oriented language in widespread use sticks to classes. Widespread variations do exist—classes in, say, C++ or Java are static, defined at compilation time, and can never change during the execution of a program, whereas those in Python or Ruby can be modified by a running program—but the use of classes is a constant.

Still, JavaScript has been enormously successful. The ability to add dynamic scripting to webpages has proven useful, and because JavaScript was the first language to be used this way—it was invented in a hurry by Netscape for Navigator 2.0—it has become the dominant force for Web scripting. Microsoft does have a pluggable scripting system supporting JScript (Microsoft's JavaScript implementation) and VBScript, with Perl and Python (among others) available as downloadable options, but only JavaScript has had the cross-platform, cross-browser uptake that's necessary to flourish in the Web environment.

I feel the need

JavaScript's wide usage has produced great pressure to make the language faster. Google's Chrome was the first browser to really tout JavaScript performance as a feature, and its V8 engine was the first to use techniques such as just-in-time (JIT) compilation to make serious improvements in JavaScript execution. Microsoft (with its Chakra engine) and Apple (with its Nitro engine) and Mozilla (with its various Monkey engines), have all followed suit. The result is that modern JavaScript engines are an order of magnitude or more faster than engines of old.

Probably the biggest single advance is the use of JIT compilation instead of interpretation. JIT compilers generate small fragments of native code so that script programs are compiled in a piecemeal fashion. When it comes to performance, JavaScript engines are some of the most advanced scripting runtimes around—generally being much faster than perl, Python, or Ruby.

But there's a big difference between "the fastest scripting language" and just plain "fast." Compared to traditional compiled languages like C++ or Fortran, or even JIT-compiled safe languages like C# and Java, JavaScript's performance looks a lot less impressive. For all the time, money, and effort that has been poured into JavaScript engines, JavaScript programs still perform at a huge disadvantage relative to those built with typical desktop development tools.

Writing a high performance scripting engine is hard to do—and writing a high performance JavaScript engine is particularly challenging. One of the things that makes it difficult is the use of prototypes instead of classes. With a class-based language, every single instance of that class has the same behavior: the same set of fields to store data and the same set of methods to perform actions. The scripting engine need only generate optimized executable code for that class once and it can then be used for every single instance of that code. Even in languages like Python, where modifications to the class can be made at runtime, the amount of code generation is limited; a modification might force the engine to throw away all the executable code it has generated so far and create new code, but that code is still shared by every instance of the class.

This approach allows very fast code to be generated. As an example, consider the simple case of accessing a field in an object. With an old-fashioned interpreted scripting engine, this might be accomplished by looking inside a dictionary mapping from field names to field values—a slow, expensive operation. With a traditional compiled language, the compiler will lay out all the fields of a class in a particular order and it will know the exact position of each field; accessing the field then becomes as quick as reading a value from memory. The position of the field will be the same for every single instance of the class, so the native code can be very simple and very fast.

In JavaScript, that's much harder to do; there are no real classes, so there's much less scope for easily sharing generated code. The overhead of generating this code will often outweigh the performance improvement that code generation provides; if you can't share the native code between lots of objects, you're often better off using the slow dictionary lookup. V8 (and the Firefox engine and, I believe, other high-performance JavaScript engines) tries to remedy this problem by generating synthetic hidden classes, which can then be shared among multiple object instances.

This works well, to a point—the modern JavaScript engines are faster than their counterparts in other languages—but it's nonetheless complex. And it's a complexity that languages with explicitly defined classes don't have to handle.

Giving developers what they want

Prototypes have also proven to be a mixed blessing for developers. Prototype-based languages are academically interesting, but it's fair to say that they've lost the battle for mainstream acceptance (if they ever really fought it). Class-based languages are taught in schools and in universities now; they're what most of the object-oriented literature and research focuses on. They're the mainstream. Developers want to use their class-based knowledge and techniques, but JavaScript doesn't directly support that. In response, many of the widely used JavaScript libraries include their own emulation of class-based objects. With classes baked directly into the language, both JavaScript engines and libraries would be easier to develop, easier to use, and faster.

The use of prototypes isn't the only thing that makes JavaScript complex, but it's probably the biggest single issue. And significantly, it's arguably also the most discardable one. If neither users of the language nor developers of engines really want prototypes, perhaps the language shouldn't be built around them after all.

JavaScript also has features (or, more commonly, it lacks features) that make development of large applications somewhat more challenging than they might be in other languages. Some of these are minor grievances—the way variable scope works, for example. Others are a bit more significant; for example, JavaScript has no built-in module system. This is a particular annoyance for programs that depend on third-party libraries, since they have no good way of ensuring that library A won't conflict somehow with library B (for example, both libraries might try to create objects with the same name).

JavaScript also lacks any kind of static typing. This is not at all unusual; dynamic typing is a feature of most scripting languages, and in this sense JavaScript is thoroughly conventional. With static typing, every variable or field has a singular, well-defined type (say, "number" or "string" or "file"). It can only store objects of that type, and the compiler will complain before you even try to run the code if you try to do something that the type cannot do—for example, if you try to divide a string by a file, or add a network socket to an integer. With dynamic typing, that's no longer the case. Variables and fields can store values of any type, and the scripting engine will only complain if it simply can't figure out what to do at runtime.

Opinions vary wildly on which approach is better. Dynamic typing tends to be easier to use and less verbose, because programmers don't need to specify type information at all. Dynamically typed languages have much of the power of interfaces, templates, and generics without ever having to suffer the complexity of those mechanisms. But static typing has its advantages, too. Detecting errors at compile time, rather than having them blow up in the user's face at runtime, is greatly preferable. Fixed knowledge of the types used in a program makes it easier to generate fast code, too. Static typing also tends to make it easier to answer questions about the code, such as "show me every part of the program that uses this class." This can be a boon to maintenance; it makes refactoring much simpler and safer.

Not everyone will agree that every JavaScript issue is a real problem. The language has its fans, though it's certainly not the case that everyone who uses it actually likes it. But everyone who uses it must surely acknowledge that some of these things are problems, even if minor ones. Some problems, such as the small built-in library and the lack of module system, can be solved with a combination of policy and third-party code. People may prefer classes to prototypes, but prototypes can be used to implement class-like systems anyway. The dynamic typing versus static typing question will never be resolved, as there's no right answer, but there's a sizeable minority at least who would prefer some kind of static typing to be possible.

The importance of Web scripting is unlikely to diminish any time soon. Web programs are only going to grow in scope and complexity. The case in favor of making the Web's scripting language better—easier to run at high speed, easier to develop, easier to test—is therefore compelling.

Hence Google's new language, Dart.

Channel Ars Technica