In this month’s column, let us continue our discussion on dynamic languages, focusing on JavaScript.
In last month’s column, we explored dynamic languages and how they are translated. While Web 2.0 accelerated the development of many dynamic languages, the one that has come to rule them all as the most popular on the Internet is JavaScript. In this month’s column, I will provide a quick overview of JavaScript, discuss the various translation engines available, and elaborate on how they differ under the hood. If you’re curious about why I’m focusing on JavaScript, I have had a few readers writing in, requesting the magazine to focus more on Internet technologies in this column. So, over the next few months, topics related to Internet technologies will be covered.
JavaScript and Prototype-Based Programming
JavaScript is ubiquitous in today’s Internet world. It runs applications all the way from your pocket smartphone to large high-end servers. It has been dubbed as the assembly language of the Internet. Most websites run JavaScript, which was designed in 1995 by Brendan Eich at Netscape. It was originally aimed at enabling non-programmers to enhance websites using client-side executable code. It is interesting to note that though the names of Java and JavaScript are similar, the two languages do not resemble each other in their behavior to any great extent. Though JavaScript is an object-oriented language, it does not support classes or encapsulation found in traditional object-oriented languages like Smalltalk, Java, C++, etc. It supports what is known as prototype-based programming.
In a language like Java, the structure and behavior of object instances is defined by the class to which the instances belong. Only the instance data is contained in the object itself. Inheritance is supported by having a derived class inheriting structure and behavior from a base class. In other words, the members and methods of the base class are inherited by (and will be available to) the derived class. This is known as class-based inheritance. Consider the following code snippet:
class Shape {
void draw();
};
class Circle : public Shape {
// Implementation
};
Circle c1;
c1.draw();
Objects of class Circle inherit the method draw
from the base class Shape. This is class-based inheritance as we know in C++, Java, etc. JavaScript does not support classes. In that case, how can you support behavior reuse? Well, JavaScript supports behavior reuse by what is known as prototype-based programming. While you can obtain behavior reuse in C++ or Java by creating an instance of a class, in JavaScript, it is done by cloning an existing object—which is known as the prototype. So how does inheritance work in prototype-based programming? Consider the following example of a vehicle object:
var vehicle = {
wheels: 4,
year: “unknown”,
make: “unknown”
};
An object in JavaScript is a set of properties, a mutable map from strings to values. The vehicle
object above has three properties: wheels
, year
, make
. Each object has a prototype field, which refers to another object. The prototype object acts as the parent for the current object. Now, you can create a new car
object by cloning the vehicle
object. (Note that there is no clone
operator in JavaScript, but you can write your own clone function.)
var car = Object.create(vehicle);
car.tyres = 4;
In this case, the prototype for the car object is vehicle, and car inherits the wheels property from vehicle. The next statement extends the properties of the car by adding a property for tyres. Note that this is different from languages like C++ or Java, where the members of an object are fixed, based on the class definition (except through inheritance) and cannot be extended at will. The point to be noted here is that properties inherited by car from its prototype are not stored in the car object; when such a property is referenced, if it is not found in car, then its prototype (vehicle) is searched. Recall that each object has a prototype field. When a property is looked up, it starts by searching the current object, then its parent, followed by its parent’s parent, until that property is found. Note that this kind of recursive search up the prototype chain can impose considerable overhead in JavaScript implementations. While there are a number of other interesting language features in JavaScript, I will not discuss them further. Instead, interested readers can find the details in any JavaScript book such as “JavaScript: The Definitive Guide” from O’Reilly Media, or “Head First JavaScript”. Next, let us explore JavaScript engines.
JavaScript Engines
JavaScript is a high-level language and an engine is needed to run the code. A JavaScript engine typically contains a parser, an interpreter, and a runtime module. The parser parses the JavaScript code into tokens, typically building an abstract syntax tree to represent the JavaScript program. The interpreter walks the abstract syntax tree and interprets the intermediate representation contained in it. While earlier versions of JavaScript engines used the simpler AST walking interpreter design, things have changed quite a bit in later engines. These parse the JavaScript to an intermediate form, which typically contains byte-codes. An interpreter translates the byte-codes and optionally supports a JIT compiler, which can translate byte-codes to native code.
There are trade-offs involved in having an interpreter rather than a JavaScript compiler. Interpreters are faster on application start-up, because they don’t spend considerable overhead in creating optimized/efficient internal representations; hence, their start-up latency is quite low. On the other hand, a JavaScript compiler can convert the intermediate representation into machine or native code—and, of course, native code execution is considerably faster than interpreted code.
The very first JavaScript engine, created by Brendan Eich, who invented JavaScript, was code-named SpiderMonkey and was implemented in C. Rhino, from Mozilla Corporation, is an open-source implementation of a JavaScript engine written in Java. The SquirrelFish Extreme engine (code-named Nitro in the Safari browser) uses interpreted byte-codes with certain hot parts of the code being translated by the JIT compiler into machine language. TraceMonkey, embedded in Mozilla’s Firefox browser, employs a similar technology. The internals of TraceMonkey are described in a paper presented at the ACM Conference on Programming Languages Design and Implementation (PLDI) 2009.
Another popular JavaScript engine is Google’s V8, which is used in the Chrome browser. V8 compiles JavaScript into native x86 code, which makes it quite fast. It also employs a number of optimizations. Recall that I had earlier mentioned the overheads in recursive property lookup following prototype chains. V8 tries to avoid this overhead by supporting fast property access, by what are known as hidden classes. These are based on the premise that an object’s properties are typically unlikely to change after it has been initialized. Hence the object structure is captured in hidden classes, and an object’s hidden class changes when a new property is added. The concept of hidden classes is not new, but inherited from the first experimental prototype language called “Self”. More details on V8’s hidden classes can be found at the V8 design documentation. I will discuss V8 internals in detail in the next column.
My ‘Must-Read Book’ for This Month
This month’s ‘must-read book’ suggestion comes from one of our readers, Suresh C, who writes, “I am basically a Windows programmer (Win32 and kernel), but once I started reading LFY and the articles in it, I started exploring Linux user-mode programming (using glibc calls) and a bit of kernel programming. The articles in LFY are very good starting points. So here is a book, which I feel is a must-read for any person who is willing to master Linux concepts and programming—’The Linux Programming Interface’ by Michael Kerrisk.” Suresh recommends the book for the following reasons:
- In-depth coverage of Linux OS concepts.
- Examples in each section to make the concepts clear.
- Explanations that are easy to understand.
If you have a favorite programming book/article that you think is a must-read for every programmer, please do send me a note with the book’s name and a short write-up on why you think it is useful, so that I can feature it in the column. This would help many readers who want to improve their coding skills.
If you have any favorite programming puzzles that you would like to discuss on this forum, please send them to me, along with your solutions and feedback, at sandyasm_AT_yahoo_DOT_com. Till we meet again next month, happy programming and here’s wishing you the very best!
Great Article.Waiting for me