January 23rd, 2010 by radu

Lately I’ve been working a lot with ExtJS and I thought it’s good to share my experience. In this post I will explain how you can achieve object orientation with the help of ExtJS Core, how easy it can be and some of its pitfalls. Learn by example

Ext.ns('myapp.cars');

(function(){
    var Car = Ext.extend(Object,{
	color: 'red',
	description: {
	    gears: 4,
	    year: 1980
	},

	constructor: function(config){
	    console.log("car constructor");
	    Ext.apply(this, config)
	},

	getColor: function(){
	    return 'actual color ' + this.color;
	},

	getMake: function(){
	    throw 'unimplemented method';
	}
    })

    myapp.cars.Car = Car;
})()

// ---- another file here: myapp.cars.Bmw.js
Ext.ns('myapp.cars');

(function(){

    var BmwCar = Ext.extend(myapp.cars.Car,{

	color: 'blue',

	constructor: function(config){
	    BmwCar.superclass.constructor.apply(this, arguments)
	},

	getMake: function(){
	    return "This is a BMW"
	}
    })

    myapp.cars.Bmw = BmwCar;
})()

First of all notice

(function(){
     // your code here
})()

We never want to populate our global “namespace” of JavaScript objects. So, we put all our code in a anonymous function, which is not only defined, but also executed. We will export only what’s needed, that is, the public API of our app.
Second, notice

Ext.ns('myapp.cars');

outside of our anonymous-function-that-hides-it-all. It is a shorthand for Ext.namespace. What it does is the following: it creates an object myapp, if it doesn’t exist. Then, it creates a property on this object (the newly created or the existing one) named cars, only if it doesn’t exist. I recomend putting a call to Ext.ns('...') at the beginning of every js file, and then, in the anonymous function call, attach what you need to into this public namespacing object. So all your files should look something like

Ext.namespace('myapp.package.subpackage');
(function(){
    var MyClass = // class definition
    ....
    // end of class definition

    //in this way we "export" MyClass to be visible publicly
    myapp.package.subpackage.MyClass = MyClass
})()

Now let’s see our class hierarchy. Use Ext.extend to extend from an existing Object. If you don’t have a specific superclass you want to extend, just extend Object. Then, as a second parameter, provide an object literal, which can contain properties and/or functions. All these properties/functions will be placed in the prototype of your newly created class, overriding (if it is the case) the properties in the prototype of the base class. Notice Ext has a special function, named constructor, which is called when objects are instantiated. If you define a constructor in the subclass, don’t forget to call the superconstructor of the base class (if you want – and probably you do).

constructor: function(config){
   BmwCar.superclass.constructor.apply(this, arguments)
}

Notice how methods from the base class are called: <current_class>.superclass.<method_name>.apply(this, arguments). You could also call it with call and other arguments, but in this way, you just forward the existing arguments, whithout having to explicitly name them.
In this example, in the constructor of the Car class we use a very powerful feature in JavaScript and ExtJS, dynamically applying properties to objects

constructor: function(config){
    console.log("car constructor");
    Ext.apply(this, config)
},

Take all the properties given in the config and apply them to the car object. For instance, you could have a code like:

var bmw = new myapp.cars.Bmw({
	color:'black',
	getColor: function(){
	    return "my custom color";
	}
});

Not only does Bmw overrides the color attribute for the car, but this bmw instance in particular overrides the color attribute to ‘black’. Also override the getColor method. This gives a tremendous flexibility, allowing more than one level of overriding! Subclass overrides superclass, and object instance(of subclass) overrides subclass. So basically you can end up building custom bmw instances, each having it’s own implementation of getColor. Generally you won’t do method overriding at instance level, but you will want to override values in the properties of an instance. So calling your constructor with a config object is very useful when wanting to set values of multiple fields at once.

Another interesting feature is overriding a method at instance level, and also using the previous implementation of the method. Take a look:

var bmw = new myapp.cars.Bmw({
	color:'black',
	getColor: function(){
	    console.log('getColor: ');
            //use previous implementation - notice apply
	    return myapp.cars.Bmw.prototype.getColor.apply(bmw) + ' custom color';
	}
});

Notice the call to the getColor method from the prototype of the Bmw class, applied to the current instance, in order to keep the context of the method call.
All these features of prototypal inheritance with JavaScript in general and ExtJS in particular, allow a great flexibility and many levels of customization.

To recap, don’t forget that when you override a method, if you want to use the implementation in the base class, you have to use something like:

 var BmwCar = Ext.extend(myapp.cars.Car,{
    getMake: function(){
        //use implementation in the superclass and forward any arguments
        BmwCar.superclass.getMake.apply(this, arguments)
    }
})

where superclass is a property set by ExtJS when creating the class, for easy reference to the superclass.

Pitfalls

There are some pitfalls you should be aware of. These appear because of the nature of the prototypal inheritance.
Don’t forget that when you write:

var subclass = Ext.extend(MySuperclass, {
    property1: value1,
    property2: value2,
    method1: function(){ ... },
    method2: function(){ ... }
});

all the properties of the config object given as the second argument to the Ext.extend function go into the prototype of the subclass. Let’s have some examples to see what can happen. Suppose for this example the myapp.cars.Car implementation above and the following Bmw implementation:

var BmwCar = Ext.extend(myapp.cars.Car,{
	model: {
	    name: 'sedan'
	}
})

Now make the following tests:

var bmw = new myapp.cars.Bmw();
var secondBmw = new myapp.cars.Bmw();

secondBmw.color = "orange";
secondBmw.model = { name: 'hatchback'}

//typical behavior
console.log(bmw.color); // outputs "red"
console.log(bmw.model); //outputs Object { name="sedan"}
var bmw = new myapp.cars.Bmw();
var secondBmw = new myapp.cars.Bmw();

//NOTICE change of model name also affects the first bmw instance
secondBmw.model.name = 'hatchback'
console.log(bmw.model); //outputs Object { name="hatchback"}

So we modify the model.name attribute on the secondBmw and it gets propagated to the first bmw instance. Why? Well, the model object belongs to the prototype of the Bmw class, so it is shared between all the bmw instances. Any change in properties of this object affects all instances of the class!
But notice in the first test, changing the model object doesn’t affect all instances of Bmw. This is because the instruction

secondBmw.model = { name: 'hatchback'}

changes the model property on the secondBmw instance to refer to a whole new object, independent of the class prototype.
So you have to be warned. It’s okay to put in the prototype properties which are JavaScript primitive types (strings, numbers), but you have to take special care when dealing with objects! They are allowed, but you should be aware of the facts explained above and handle with care.

This is a summary of prototypal JavaScript inheritance with ExtJS. Play with it, try it, use it!
Have fun!

8 Responses to “Object oriented JavaScript with ExtJS”

Great post on Ext JS, I hope to see more like it on your blog.

Nick Searcy says:

Hello there :) me and a buddy are currently working on a project on this subject for a school project and your article is really useful :D

radu says:

@Nick Glad it helped! ExtJS is really great! It offers you a great deal of flexibility and is very extensible. Good choice!

haidong says:

thanks very much .it’s very useful

Alex says:

I ran into this exact problem – I had two different grids that only differed in the url used by the store, and they would end up with the same url for the store whenever I tried moving all the common configuration into a base class. Now that I know why (the store was being added to the prototype instead of the individual grid instance), it’s easy to fix – add the configuration to the specific instance within the initComponent method using Ext.apply().

Using your example, it would look like this:

var BmwCar = Ext.extend(myapp.cars.Car,{
initComponent: function() {
Ext.apply(this, {
model: {
name: ’sedan’
}
});
… and so on …
}
})

Each instance of BmwCar now has its own instance of “model”, which is what I needed.

radu says:

@Alex Hi Alex,

I’m glad you found the issue and I hope this post was helpful. Yes, it’s tricky in the beginning, but when the behaviour is understood, it becomes natural.

I had a similar problem using Ext.util.TaskRunner. I tried to extend it with

var MyRunner = Ext.extend(Ext.util.TaskRunner, { start: function(){ /* my custom start method */ } })

and the overriden start method would never be called.
This is because Ext.util.TaskRunner does not extend Component but is a JavaScript function, and when I tried

var myRunner = new MyRunner()
myRunner.show()

it didn’t work.
This is because the Ext.override puts the functions (or other configs that you provide) on the prototype, which has less precedence that members directly existing on the “this” object. So when calling .show(), it didn’t call my method, as it was in the prototype, but called the already existing one in the object.

Thanks for the visit and the comment

Clint says:

Hi Radu, thanks for the helpful post. However, I’m confused about why you define car this way:

var Car = Ext.extend(Object,{ … });
myapp.cars.Car = Car;

instead of just doing this:

myapp.cars.Car = Ext.extend(Object,{ … });

What is the difference between this two ways of defining the Car class in the myapp.cars namespace?

Thanks for clarifying!

radu says:

Hi Clint,

Thanks for the comment. Basically it is the same thing, it’s just an alias. The reason I am writing it this way is for the code to be shorter. If you have a look at the definition of BmwCar, you can see the name of the class is also used in the constructor, in order to call the superclass constructor. If you would use only the fully qualified name (that is: myapp.cars.BmwCar) the code would be much longer and a bit more difficult to refactor when renaming package names. In the end, the only difference is clearer code (if you use the name of the class more than once in the file where you make this alias).

Leave a Reply