Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

JavaScript as a Foundation Language for Your Web Apps : Page 2

Get an overview of the JavaScript programming language, from its basic structure and underlying paradigms to its high-level constructs, concepts, libraries, and functionalities—all of which are powering the current generation of web applications.


advertisement
Objects vs. Prototypes
If you are more familiar with object-oriented programming (OOP), you can adopt the OOP style in JavaScript as well. JavaScript does not support classical class-based OOP but a variant called prototype-based programming. The main difference between the two is that class-based languages such as Java distinguish between classes, which define the behavior exposed by the entities you define (i.e., the class methods), and objects, which contain the data such behaviors can be applied to (i.e., instances of the class).

Prototype-based languages do not have classes. They define only objects and the semantics surrounding how new objects can be created or derived from existing ones. Any two objects can be linked together into a relationship, where one object represents the "prototype" for the other in the sense that it defines the stub upon which the object builds its functionalities. Such links are used for data access and function resolution in a way that resembles traditional class inheritance: Each object delegates to its prototype whenever it is asked to access a variable or a function it doesn't know. This paradigm, adopted by JavaScript, is called "delegation." (Refer to this introduction to prototypes for a more thorough explanation.)

Listing 1 and Listing 2 illustrate the differences between class-based and prototype-based approaches. Listing 1 shows the definition and usage of a parent and child class in Java. Listing 2 shows the same definition in JavaScript using prototypes.



Listing 2 actually demonstrates a number of language features. First of all, look at the definition of the Compiler object. It is an anonymous function that operates on the this variable. The object itself is created using the new keyword. Behind the scenes, JavaScript creates an empty object when it encounters the new keyword, and then it invokes the anonymous function as a constructor, assigning the newly created object to the this variable so that the function can proceed to initialization. The code contains some more subtleties, but that is the high-level view.

Notice also how you create methods by attaching functions to objects, just like you would with regular variables. JavaScript functions are objects. This is one of the functional aspects of JavaScript, which in part behaves like pure functional languages such as Lisp.

The Compiler Object with a Better Use of Prototypes
The following statement from Listing 2 defines the delegation relationship between the two objects:

OptimizedCompiler.prototype = new Compiler();

Remember that OptimizedCompiler is a function, and each function contains a prototype attribute that you can use to explicitly declare who its prototype is. Even though this is formally an has-a relationship, because of the way JavaScript delegation works, it ends up working in the same way you would expect classical inheritance to work.

Notice also that the code assigns a newly instantiated object—not a class—as the prototype. This shows that JavaScript prototypes are mutable at any time, a feature that you will leverage later. Prototype delegation is also dynamic. After you establish a relationship, whenever you change some features of the prototype object, all the dependent objects that delegate to it will expose the new features immediately.

Because each function always has its prototype defined, you can move method definitions on it. Additionally, since JavaScript does not have private scopes (although you can mimic them using closures, which you will learn about soon), you can also remove accessor methods. With these change made, the Compiler object definition now reads as in Listing 3.

Object Literals in JavaScript
If you don't need the benefits of prototypes and inheritance, you can simplify object creation by using literals. JavaScript objects are nothing more than collections of key value pairs, and you can create them using a simplified syntax. By using literals, you can also skip the definition of a constructor, which is superfluous in this example.

JavaScript is a dynamically typed language and therefore supports duck typing: You don't have to declare function expectations in term of types, but rather in term of capabilities. You are free to pass whatever object you want to a function, as long as it fulfills the contract the function is going to exercise on the object.

By applying these two concepts, the example collapses into the following reduced notation:

// define the object literal var c = { languageName : "Java", optimizationLevel : 4, compile : function(code) { /* compile the code */ } }; document.write( "Got a " + c.languageName + " with optimization level " + c.optimizationLevel); // invoke the compile function on the object. // JavaScript doesn't require a static type // to identify the function and invoke it. var complexPieceOfCode = " ... "; c.compile(complexPieceOfCode);

Remember that all JavaScript prototypes are mutable. You can leverage this even to enrich standard classes as follows, where the even() method is added to the Number class:

Number.prototype.even = function() { return this % 2 == 0; }; e = new Number(4); o = new Number(5); e.even(); // true o.even(); // false

Functions and Functional Programming
As you have seen, functions are first-class citizens in the JavaScript landscape. They are objects and therefore extensible and customizable. You also can pass them around as method parameters and assign them to variables.

JavaScript also supports closures. Closures represent the ability of functions to retain the context in which they were created, so that when invoked later they can still access the state information available within the scope at creation time.

Because such scopes are accessible only to the function to which they are associated, they offer a perfect way to avoid polluting the global namespace with symbols that are used only in a limited area. They also provide a way to implement private scopes (for example, limiting the visibility of some object fields).

These functional aspects add great expressive power to the language and allow you to define rich behaviors and patterns (callbacks, handlers, partials, currying, and many others) that are typical of more formalized functional languages such as Lisp. Listing 4 shows a mixed example of all these function features.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap