More in the series for C# programmers learning JavaScript, with Firebug as our scratchpad.

In this episode, we let go of class inheritance. Bye!

I know, it's taken you years to understand class inheritance until you could do it in your sleep. It took me years as well. We've had it engrained in us for a while: design a class to model some object in our problem space, worry about encapsulation and behavior, think about creating descendants that increase the specificity of our first more-general class, and perhaps keep on going to produce higher, more-specific classes. We exult in those class models and in the type-safety they give us.

Some of us, as we learn more, start to find that the implementation inheritance model gets to be too restrictive and too wordy. We start experimenting with the interface inheritance model where, essentially, we think about inheritance of behavior rather than of behavior plus data. Our class models become shallower and not filled with classes than descend from classes which descend from others and so on all the way down. We use delegation of the interface as a coding model.

But JavaScript doesn't do all that. It has objects inheriting from objects. Period. Furthermore those objects are dynamic in nature: we can add new members or remove them at a moment's notice. No, this is not the description of anarchy, but a realization that perhaps classes are just too restrictive. Certainly, you can use libraries like Prototype that try to provide you with a class model type structure to your applications, but it is far better to just embrace the way that JavaScript works.

The problem is that JavaScript is conflicted in what you do for inheritance. Despite the speech from the high ground just then that JavaScript objects inherit from other JavaScript objects, there's so simple support for it in the language. Instead we have this unholy trinity of constructors, prototypes and the new keyword that hide object inheritance in order to smooth the way for us developers coming from a classical object-oriented language.

Duping an object

So let's add a new function to give us a direct way to create an object from another. Here's Douglas Crockford's Object.create method (so named because this is going to be part of ECMAScript 3.1 (ES3.1), the new version of JavaScript, due "soon").

if (typeof Object.create !== 'function') {
    Object.create = function(o) {
        function F() { }
        F.prototype = o;
        return new F();
    };
}

Here, Object is the constructor for objects (which we tend not to use as such, since the object literal syntax is so much more convenient), and we're going to try to add a method called create to it. So we test to see if it exists yet (so the if block won't execute once ES3.1 hits the streets), and, if not, we create it. The function takes an object o, and encapsulates declaring a do-nothing constructor, setting the prototype object of this new constructor to the passed in object, calling the constructor to create a new object, and returning it. What we get in the end of these shenanigans is a new object that inherits from the source object.

At a stroke we avoid several problems. We never have to call a constructor again and remember to use the new keyword (else we clobber the global object). We can just forget about new altogether. We can equally forget about constructors. We've boiled inheritance down to defining an object to do some interesting things, and then using Object.create to create dupes of that object that we can use however we want (including adding new members and then using with Object.create).

Example: Expression Parser

I wrote a simple expression parser for PCPlus a while back, and posted the article here together with the C# code I'd used. I recommend you go read it (it won't take long) and briefly browse the source code there so that you are familiar with what I'm going to be talking about.

We're going to rewrite it in JavaScript.

Writing RPN expression token objects

The first thing that I found restrictive with the C# code is that I wrote it to deal with single digit numbers. Wow, indeed, talk about restrictive. Whew! The reason for this was that it meant I could use a simple string to store the RPN expression and it made the whole article easier to understand without drowning the reader in unnecessary complications. So let's fix that to begin with.

An RPN expression is a sequential list of tokens, read from left to right, of which there are only two types: a number and an operator. There are no parentheses, or anything like that. We start off creating a token object that can report back what it is:

var rpnToken = {
    isOperator: function() { return false; }
};

Since we'll be dealing with numbers more often than operators, I made the isOperator method return false by default. (To recap on the syntax: rpnToken is an object containing a single member — a method — and I'm initializing it as an object literal.)

The next thing I wrote was a function that creates a number token:

var makeNumber = function(value) {
    var node = Object.create(rpnToken);
    node.value = value;
    return node;
};

It takes a value, creates an object that inherits from the rpnToken object, and defines a new member called value that holds the value passed in. Since the returned object inherits from rpnToken, it will inherit the isOperator method, and we already know that returns false by default.

Now let's create a similar function that creates an operator token:

var makeOperator = function(func) {
    var node = Object.create(rpnToken);
    node.isOperator = function() { return true; };
    node.evaluate = func;
    return node;
};

This works in the same way as makeNumber, but is slightly more complicated. Again we create a new object that descends from the rpnToken object. We override the isOperator method to return true. We pass in a function that will perform whatever operation this operator does and we save that as the evaluate method.

Since we're going to be dealing with the same four operators over and over again, I decided to create an object to store the canonical four as constants:

var operator = {
    "+": makeOperator(function(lhs, rhs) { return lhs + rhs; }),
    "-": makeOperator(function(lhs, rhs) { return lhs - rhs; }),
    "*": makeOperator(function(lhs, rhs) { return lhs * rhs; }),
    "/": makeOperator(function(lhs, rhs) { return lhs / rhs; })
};

This is slightly weird in that I'm using the actual operators as property names (so I can reference them later on when parsing the algebraic expression, a bit like this: op = operator[token];). Note how I create the functions to pass to the makeOperator function — you can guess that rhs and lhs in each case are going to be normal numbers.

Writing an RPN expression object

To make sure I was at least getting this all correct, I wrote my first cut at the rpnExpression object:

var rpnExpression = function() {
    var expr = [];
    return {
        add: function(token) {
            expr.push(token);
        },

        evaluate: function() {
            var stack = [];
            for (var i = 0; i < expr.length; i++) {
                var token = expr[i];
                if (token.isOperator()) {
                    var rhs = stack.pop();
                    var lhs = stack.pop();
                    stack.push(token.evaluate(lhs, rhs));
                }
                else {
                    stack.push(token.value);
                }
            }
            return stack.pop();
        }
    };
}();

Although it looks at first glance that rpnExpression is a function, it is not. It is an object. It is being set to the return value from a function: notice the execution operator (the parentheses) on the last line. The function is merely there as a one-off anonymous function that returns an object, and furthermore one that is executed immediately. I did it this way so that I could gain the private member called expr.

So, the function declares a private field and then returns an object with two methods. The first method merely pushes new tokens onto the end of the private expression field. I'm making use here of the standard methods on an array that make it look like a stack.

The second method defines the evaluate method. First of all it declares a stack (yes, it's an ordinary array: you weren't fooled), and then starts reading the tokens present in the expression. If the current token is an operator, two numbers are popped off the stack, the result evaluated according to the operator, and it's pushed back onto the stack. If, on the other hand, the token was a number, we push its value on the stack.

At the end of the expression, we pop the final number off the stack and return it.

Testing the code so far

We really should make sure that all works, just as we would if we'd been writing in C#, so I wrote a quick set of tests:

var numberEquals = function(x, y) {
    return Math.abs(x - y) < 0.00001;
};

rpnExpression.add(makeNumber(1.2));
rpnExpression.add(makeNumber(3.4));
rpnExpression.add(operator["+"]);
console.log(numberEquals(rpnExpression.evaluate(), 4.6));
rpnExpression.clear();

rpnExpression.add(makeNumber(1.2));
rpnExpression.add(makeNumber(3.4));
rpnExpression.add(operator["-"]);
console.log(numberEquals(rpnExpression.evaluate(), -2.2));
rpnExpression.clear();

rpnExpression.add(makeNumber(1.2));
rpnExpression.add(makeNumber(3.4));
rpnExpression.add(operator["*"]);
console.log(numberEquals(rpnExpression.evaluate(), 4.08));
rpnExpression.clear();

rpnExpression.add(makeNumber(1.2));
rpnExpression.add(makeNumber(3.4));
rpnExpression.add(operator["/"]);
console.log(numberEquals(rpnExpression.evaluate(), 0.3529412));
rpnExpression.clear();

(The clear method is an extra method that merely resets the private expr field.) Notice that I can't compare the results of the calculations to the expected values directly since the number type in JavaScript is a double — we have to compare equal to an approximation.

Next time we'll continue the work, but already you should be seeing how the code I've written doesn't use a class-based inheritance model despite the fact that we would have written it as such in C#. Notice how the JavaScript code is made much cleaner and legible as a result. It's also way less wordy and noisy (I'd have probably been using generics for the RPN expression class).

To recap: object inheritance involves making some object that has interesting behavior (the rpnToken above), and then creating other objects that inherit from it (the number and operator tokens above) and that can have other properties and methods defined on them.

Album cover for Greatest Hits Now playing:
Human League - Mirror Man
(from Greatest Hits)



Posts on similar topics...

8 Responses

  • Tue 03 Mar 2009
  • 7:04 AM
  •  avatar #1

Dew Drop - March 3, 2009 | Alvin Ashcraft's Morning Dew said...

Pingback from Dew Drop - March 3, 2009 | Alvin Ashcraft's Morning Dew

  • Tue 03 Mar 2009
  • 7:58 AM
  •  avatar #2

JavaScript for C# programmers: object inheritance (part 1) : Algorithms for the masses - julian m bucknall said...

Thank you for submitting this cool story - Trackback from DotNetShoutout

  • Tue 03 Mar 2009
  • 10:58 AM
  •  avatar #3

Daniel said...

Your brief overview of Object.create() was useful, but I wish you'd go into more detail.

P.S. Your numberEquals(x,y) method returns true every time y is greater than x.

numberEquals(-1, 5) => true

  • Tue 03 Mar 2009
  • 11:20 AM
  • julian m bucknall avatar #4

julian m bucknall said...

Daniel: Damn, you're right. Trust me to work too late and write a test that's always green. (Now fixed using Math.abs.)

For a further explanation of why Object.create "works" you'll have to read my previous entries in this series I'm doing, especially the one on the basics of prototypes.

Cheers, Julian

  • Tue 03 Mar 2009
  • 5:19 PM
  •  avatar #5

My Bad Attitude » JavaScript for C# programmers: object inheritance (part 1) said...

Pingback from My Bad Attitude » JavaScript for C# programmers: object inheritance (part 1)

  • Fri 06 Mar 2009
  • 9:38 PM
  •  avatar #6

JavaScript for C# programmers: object inheritance (part 2) said...

Continuing to learn JavaScript from the viewpoint of a die-hard C# programmer, using Firebug as our test engine. In this episode, we continue writing the expression evaluator. (Please review part 1 before continuing.) You might want to have an extra browser

  • Wed 06 May 2009
  • 4:10 PM
  •  avatar #7

Ralph M. Holman » Blog Archive » Lisaac said...

Pingback from Ralph M. Holman » Blog Archive » Lisaac

  • Wed 16 Dec 2009
  • 3:15 AM
  •  avatar #8

Michael said...

I observed one thing about Base2 and Prototype. When you call a chain method and you reach the base method, inside the method you have access to the derived object members using “this”. In my opinion once the call is navigating thru the chain on each base class the user should not be able to access the derived class members based on OOP definitions. I also tried a different approach of JavaScript inheritance, it can make use of closures or prototype, it’s up to the way you want to implement it http://dotnetcaffe.blogspot.com/2009/12/javascript-inheritance.html. In my case if the base class constructor requires parameters they will be passed from the derived class. Here is an example:

  var Human = MG.Class.Create({
    Gender: null,
    constructor: function(gender) {
      this.Gender = gender;
    },
    GenderString: function() {
      return this.Gender == ‘F’ ? “female” : “male”;
    },
    ToOverride: function() { return “Called from Human”; }
  }, null, null);
  
  var Individual = MG.Class.Create({
    Unique: null,
    constructor: function(unique) {
      this.Unique = unique;
    },
    IsUnique: function() {
      return this.Unique ? “Yes” : “No”;
    },
    ToOverride: function() { return “Called from Individual which is ” + (this.Unique ? “unique” : “not unique”) + ” / ” + this.base(); }
  }, Human, null);
  
  var objHuman = new Human(”F”);
  var objIndividual = new Individual(true, “M”);

In my opinion also a check for parameters of the base constructor should be added. If the derived object does not provide on instantiation the correct number or parameters for the base, then an exception should be triggered.

Leave a Response

Some MarkDown is allowed, but HTML is not. (Click to learn more.)

  • Emphasize with italics: surround word with underscores _emphasis_
  • Emphasize strongly: surround word with double-asterisks **strong**
  • Link: surround text with square brackets, url with parentheses [text](url)
  • Inline code: surround text with backticks `IEnumerable`
  • Unordered list: start each line with an asterisk, space * an item
  • Ordered list: start each line with a digit, period, space 1. an item
  • Insert code block: start each line with four spaces
  • Insert blockquote: start each line with right-angle-bracket, space > Now is the time...

Extras

Search

About Me

I'm Julian M Bucknall, an ex-pat Brit living in Colorado, an atheist, a microbrew enthusiast, a Volvo 1800S owner, a Pet Shop Boys fanboy, a slide rule and HP calculator collector, an amateur photographer, a Altoids muncher.

DevExpress

I'm Chief Technology Officer at Developer Express, a software company that writes some great controls and tools for .NET and Delphi. I'm responsible for the technology oversight and vision of the company.

Validation

Validate markup as HTML5 (beta)     Validate CSS

Bottom swirl