Skip to main content

Crossing borders: JavaScript's language features

Exploring the ugly duckling of programming languages

Bruce Tate (bruce.tate@j2life.com), President, RapidRed
Bruce Tate
Bruce Tate is a father, mountain biker, and kayaker in Austin, Texas. He's the author of three best-selling Java books, including the Jolt winner Better, Faster, Lighter Java. He recently released From Java to Ruby and Rails: Up and Running. He spent 13 years at IBM and later formed the RapidRed consultancy, where he specialized in lightweight development strategies and architectures based on Ruby, and in the Ruby on Rails framework. He is now the CTO of WellGood LLC, a company that is forming a marketplace for nonprofit organizations and charitable giving.

Summary:  JavaScript is often ridiculed as the black sheep of programming languages. The development tools, a complicated and inconsistent document object model for HTML pages, and inconsistent implementation in browsers contributes to that sentiment. But JavaScript is much more than a toy. In this article, Bruce Tate explores JavaScript's language features.

View more content in this series

Date:  19 Dec 2006
Level:  Introductory
Activity:  6298 views

Nearly every Web developer has cursed JavaScript at one time or another. The beleaguered language sags under the weight of a complex programming model called the document object model (DOM), poor tools for implementation and debugging, and inconsistent browser implementations. Until recently, many developers had all but written off JavaScript as a necessary evil at best or a toy at worst.

But JavaScript is becoming increasingly important, and it remains the most broadly available scripting language for Web development. Some industry leaders are taking a fresh look at JavaScript, driven by the resurgence of its use. Programming techniques like Ajax (Asynchronous JavaScript + XML) make Web pages more interactive. Full Web development frameworks, such as Apache Cocoon, make increasing use of JavaScript beyond simple scripts on a Web page. A JavaScript derivative called ActionScript powers Macromedia's Flash client-side framework. And Rhino, the implementation that runs in the JVM, makes JavaScript available to Java™ developers as a first-class scripting language (see Resources).

My friend and colleague Stuart Halloway, one of the foremost experts on Ajax, begins a JavaScript class with a provocative statement: "By 2011, we will recognize JavaScript as a language with a better set of features for developing modern applications." He then says that JavaScript programs are often 10 times as dense as similar Java programs and goes on to show the language features that make it so.

About this series

In the Crossing borders series, author Bruce Tate advances the notion that today's Java programmers are well served by learning other approaches and languages. The programming landscape has changed since Java technology was the obvious best choice for all development projects. Other frameworks are shaping the way Java frameworks are built, and the concepts you learn from other languages can inform your Java programming. The Python (or Ruby, or Smalltalk, or ... fill in the blank) code you write can change the way that you approach Java coding.

This series introduces you to programming concepts and techniques that are radically different from, but also directly applicable to, Java development. In some cases, you'll need to integrate the technology to take advantage of it. In others, you'll be able to apply the concepts directly. The individual tool isn't as important as the idea that other languages and frameworks can influence developers, frameworks, and even fundamental approaches in the Java community.

In this article, I'll explore the features of JavaScript that make it so wonderfully attractive:

  • Higher-order functions. A high-order function is one that either takes functions as arguments or returns a function. This feature lets JavaScript programmers manipulate functions in ways that the Java language can't.

  • Dynamic typing. By delaying binding, JavaScript can be more concise and flexible.

  • A flexible object model. JavaScript's object model uses a relatively uncommon approach to inheritance -- called prototypes -- instead of the Java language's more common class-based object model.

You probably recognize the dynamic typing model, functional programming in the form of higher-order functions, and an open object model as ideas I've presented in other Crossing borders articles. If you've not done any serious JavaScript development, you're likely thinking that these features belong in serious languages such as Python, Lisp, Smalltalk, and Haskell instead of "toy" languages like JavaScript. So I'll back these concepts up with real code examples.

Getting started

You don't need to do anything to set up JavaScript. You're reading this article in your browser, so you've already got it. All of the programming examples in this article run in most browsers. I'll use Firefox.

I'll load simple Web pages with embedded JavaScript wrapped in <script type='text/javascript'> and </script> tags. Listing 1 displays the Hello, World text:


Listing 1. Hello, world

<script type='text/javascript'>
alert('Hello, World.')
</script>

To run the code, just create a file called example1.html, copy Listing 1 into the file, and load the file in your browser. (See Download for all the example HTML files for this article.) You'll notice that the code immediately executes each time you reload the page.

alert is a function invocation, with a single string as a parameter. Figure 1 shows the code in Listing 1 pops up an alert box with the words "Hello, World." If the code is in the HTML body (you've specified no body, but browsers are tolerant of malformed HTML, and all of the page is treated as the body by default), the JavaScript executes immediately upon page load.


Figure 1. Hello, world
Hello, World.

If you need to delay execution, you can declare JavaScript functions in the HTML <head> element, as in Listing 2:


Listing 2. Delaying execution

<head>
    
    <script type='text/javascript'>
        function hello() {
            alert('Hello, World.')
        }
    </script>
</head>
<body>
    <button onclick="hello();">Say Hello</button>
</body>

Type the code in Listing 2 into an HTML file, load the file in your browser, and click the Say Hello button for the result shown in Figure 2:


Figure 2. Delaying execution
Delaying execution

Higher-order functions

From Listing 2, you get a taste of JavaScript's capabilities for manipulating functions. I pass the name of the function to the HTML button tag and exploit HTML's built-in event model. Using JavaScript, I'll often store functions in variables or arrays. (Later, under Object models, you'll see that the JavaScript object model strategy uses this technique extensively.) For example, look at Listing 3:


Listing 3. Manipulating functions with variables

<head>
    
    <script type='text/javascript'>
        hot = function hot() {
            alert('Sweat.')
        }
        cold  = function cold() {
            alert('Shiver.')
        }
        
        function swap() {
            temp = hot
            hot = cold
            cold = temp    
            alert('Swapped.')
        }
    </script>
</head>
<body>
    <button onclick="hot();">Hot</button>
    <button onclick="cold();">Cold</button>
    <button onclick="swap();">Swap</button>
</body>

Functions are first-class objects in JavaScript, and I can manipulate them freely. First, I declare two functions, hot and cold. I store each in a variable. Clicking the Hot or Cold button invokes the appropriate function, generating an alert. Next, I declare a function that swaps the values of the Hot and Cold buttons, attaching this function to a third button that displays the alert shown in Figure 3:


Figure 3. Manipulating functions

This example shows that I can treat a function just as I treat any other variable. C developers think of this concept as a function pointer, but JavaScript's notion of higher-order functions is much more powerful. This feature gives JavaScript programmers the ability to deal conceptually with actions, or functions, as easily as they deal with other variable types.

Using a function as a function argument, or returning a function as a value, elevates this abstraction into the realm of higher-order functions. Listing 4, a slight modification of Listing 3, shows a higher-order function that returns a function:


Listing 4. Higher-order functions

<head>

    <script type='text/javascript'>

        function temperature() {
            return current
        }

        hot = function hot() {
            alert('Hot.')
        }

        cold  = function cold() {
            alert('Cold.')
        }

        current = hot

        function swap() {
            if(current == hot) {
              current = cold
            } else {
              current = hot
            }
        }
    </script>
</head>
<body>
    <button onclick="funct = temperature()();">Temperature</button>
    <button onclick="swap();">Swap</button>
</body>

This example solves a common problem: how do you attach changing behavior to a user interface event? With a higher-order function, it's easy. The temperature higher-order function returns the value of current, which has either the hot or cold function. Look at the rather odd-looking invocation of the function: temperature()(). The first set of parentheses is needed to invoke the temperature function. The second invokes the function returned by temperature. Figure 4 shows the output:


Figure 4. Higher-order functions
higher-order functions

Higher-order functions form the foundation of functional programming, which most believe represents a higher level of abstraction than pure object-oriented programming. But the strengths of JavaScript don't begin and end with higher-order functions. JavaScript's dynamic typing is particularly well-suited for UI development.


Dynamic typing

With static typing, the compiler can check the values of arguments, variables, or return values against values allowed for a given operation. The benefit is additional error checking by a compiler. Also, static typing provides more information for tools such as IDEs, allowing features such as better code completion. However, static typing has several drawbacks:

  • You must declare your intentions earlier, which often leads to reduced flexibility. For example, changing a Java class also changes that class's type, forcing a recompile. In contrast, Ruby allows open classes, but changing a class also changes that class's type.

  • You must often enter more code to express equivalent ideas. For example, you must include type information with arguments, return values with functions, and types for all variables. You must also declare all variables and explicitly convert between types.

  • The compile-deploy cycle often takes longer than development cycles for similar dynamic languages, though tools can sometimes mitigate this problem somewhat.

Static typing often works well with languages for building middleware or operating systems. UI development often demands better productivity and high flexibility, so dynamic typing is often preferred. I definitely recognize the danger. Web developers who have spent any time at all with JavaScript have pulled their hair out over a mistyped variable name that could well have been caught by a compiler. But the benefits, too, are undeniable. I'll show a couple of examples.

First, consider an object. In Listing 5, I create a new object and access a nonexistent attribute called color:


Listing 5. Introducing an attribute

<script type='text/javascript'>
    blank_object = new Object();
    blank_object.color = 'blue'
    alert('The color is ' + blank_object.color)
</script>

When you load the application and execute it, you get the result in Figure 5:


Figure 5. Introducing attributes
Introducing attributes

JavaScript doesn't complain that the blue attribute does not exist. Proponents of static languages are horrified at this example, seeing the ripe potential for errors. Although it may make you feel dirty, you can't deny the appeal. You can quickly introduce attributes. And combining this example with the earlier examples in this article, you can also introduce behavior. Remember, variables can hold functions! So based on the dynamic typing and higher-order functions, you can introduce arbitrary behavior to your classes at any time.

I can easily rewrite Listing 5 as Listing 6:


Listing 6. Introducing behavior

<script type='text/javascript'>
    blank_object = new Object();
    blank_object.color = function() { return 'blue'}
    alert('The color is ' + blank_object.color())
</script>

You can see how I can rapidly move between concepts in JavaScript, with dramatic changes in meaning -- like introducing behavior versus data -- with only minor changes in syntax. Part of the power that you see -- which is also a great weakness -- is the language's great malleability. In fact, the object model in the language itself is just one example of just how far you can bend JavaScript.


Object models

By now, you should be getting a real appreciation that JavaScript is more than a toy. In fact, many have used its object model to create surprisingly sophisticated, well-designed, object-oriented software. But the object model, especially for inheritance, is not what you're used to.

The Java language is class-based. When you build applications, you build a new class as a template for all objects. Then, you call new to instantiate that template, creating a new object. In JavaScript, you instead create a prototype, which is an instance that creates all future objects.

I'll break away from the abstract to show you some working code. To start, Listing 7 creates a simple Animal having an attribute of name and an action of speak. Then, other animals will inherit from this base.


Listing 7. Creating a constructor

<script type='text/javascript'>        
function Animal() {
    this.name = "nobody"
    this.speak = function () {
        return "Who am I?"
    }
}

myAnimal = new Animal();
alert('The animal named ' + myAnimal.name + 
      ' says ' + myAnimal.speak());

</script>

Listing 7 produces the example in Figure 6:


Figure 6. Creating a constructor
constructor

To a Java developer, the code in Listing 7 looks unpredictable and strange. Heck, to most JavaScript developers, who don't build their own objects, that code looks unpredictable and strange. Perhaps I can inject some sanity to the picture.

Really, you're missing only three pieces of information. First, JavaScript represents objects as nested functions. That means the definition of Animal in Listing 7 is valid syntax. Second, JavaScript bases object construction on a prototype, or an existing instance of an object, rather than a class template. funct() is an invocation, but new Animal() constructs an object based on the prototype in Animal. Finally, in JavaScript, objects are merely a collection of functions and variables. No type is associated with the object, so you can modify this structure freely.

Go back to Listing 7. You can see that JavaScript defines the new object, myAnimal, based on the prototype you specify in Animal. You can then use the attributes and functions in your prototype, or even redefine the functions or attributes. This flexibility can be maddening to Java developers, who are not accustomed to this behavior, but it's quite a powerful model.

I'm going to turn things up a notch now. You can use the instance variable called prototype to specify the basis for your object. You'll set a prototype instance variable to point to the parent in the inheritance chain. If you set prototype, objects you create will inherit attributes and functions for any that you fail to specify. In this way, you can simulate the object-oriented concept of inheritance. Take Listing 8, for example:


Listing 8. Inheritance with prototypes

<script type='text/javascript'>        

Animal = function() {
    this.name = "nobody"
    this.speak = function () {
        return "Who am I?"
    }
}
Dog = function() {
  this.speak = function() {
    return "Woof!"
  }
}
Dog.prototype = new Animal();

myAnimal = new Dog();
alert('The animal named ' + myAnimal.name + 
      ' says ' + myAnimal.speak());
      </script>

In Listing 8, I create a Dog prototype. I base that prototype on Animal. Dog redefines the speak() method but does nothing for the name() method. Then, I set the prototype of Dog to Animal. Figure 7 shows the result:


Figure 7. Inheritance with prototypes
inheritance

This is how JavaScript resolves any reference to attributes or methods:

  • JavaScript creates an instance based on your original prototype, defined in your constructor. Any method or attribute you reference will use the original copy you generate.

  • You can redefine the variables in your object just like any other variable. By doing so, you will, of course, change your object. So any attributes or functions you explicitly define take precedence over those defined in your original prototype.

  • If you explicitly set the instance variable called prototype, JavaScript will look in this instance for any undefined instance variables or attributes. This lookup is recursive: if the instance defined in prototype cannot find an attribute or function, it will look in its prototype, and so on.

So what's the inheritance model for JavaScript? It all depends on how you define it. You define the inheritance behavior so you can override it. Ultimately, and surprisingly, JavaScript is more of a functional language than an object-oriented one, and it uses some clever syntax and semantics to simulate highly sophisticated behaviors. The object model is strikingly flexible, fully reflective, wide open, and extremely powerful. Some would say it is too flexible. I say use the right tool for the job.


Wrap up

The JavaScript object model builds on the other capabilities of the language to support massive libraries such as Dojo (see Resources). The flexibility lets each framework change the object model in subtle ways. In some ways, this kind of flexibility is a great weakness. You can imagine the interoperability nightmares that could result (though the language's flexibility mitigates these problems in some ways).

In other ways, this flexibility is also a great blessing. The Java language has long suffered from escalating complexity because the base object model is not flexible enough to be extended. Great open source projects and new technologies such as aspect-oriented programming, the Spring programming framework, and bytecode-enhancement libraries add mountains of code to the things a typical enterprise developer must learn to wield the Java language successfully.

In the end, JavaScript's great flexibility does give you a glimpse into the power of higher-order languages. You may not choose to make the same set of compromises in every project or even most of them. But knowing a language's strengths and weaknesses -- based on information beyond hype and public opinion -- can give you a better handle on when to use it and when to pass. And when you do find yourself modifying that maddening JavaScript Web widget, at least you'll be able to play to the language's strengths. Until next time, keep crossing borders.



Download

DescriptionNameSizeDownload method
Sample HTML files for this articlej-cb12196.zip3KB HTTP

Information about download methods


Resources

Learn

Discuss

About the author

Bruce Tate

Bruce Tate is a father, mountain biker, and kayaker in Austin, Texas. He's the author of three best-selling Java books, including the Jolt winner Better, Faster, Lighter Java. He recently released From Java to Ruby and Rails: Up and Running. He spent 13 years at IBM and later formed the RapidRed consultancy, where he specialized in lightweight development strategies and architectures based on Ruby, and in the Ruby on Rails framework. He is now the CTO of WellGood LLC, a company that is forming a marketplace for nonprofit organizations and charitable giving.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology, Web development
ArticleID=181345
ArticleTitle=Crossing borders: JavaScript's language features
publish-date=12192006
author1-email=bruce.tate@j2life.com
author1-email-cc=bruce.tate@j2life.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers