Any function in JavaScript can be used to create custom object classes, simply by calling it using the keyword
new
. When called in this way, the special variable
this inside the function references the new object that is being constructed (it normally refers to the 'current' object, which is usually
window, except inside methods). The function should not return a value. The following function can be used as demonstrated to create an object of class
myobject:
function myobject() {
this.containedValue = 0;
this.othercontainedValue = 0;
this.anothercontainedValue = 0;
}
var mything = new myobject();
And there you go,
mything is now an instance of class
myobject. It will have the following properties, all of which will be 0:
- mything.containedValue
- mything.othercontainedValue
- mything.anothercontainedValue
You could also now write this:
myobject.prototype.newContainedValue = someValue;
This will cause all instances of class
myobject will have the property
newContainedValue with value
someValue.
Use the instanceof operator to find out if your object is an instance of a given object class:
if( myvar instanceof Array ) { ... }
if( myvar instanceof myobject ) { ... }
Creating an object with methods
Now I will give you an example showing you how to create methods for your objects. As an example, I will create a circle as my object. The methods will be
nameOfCircle.retArea()
- Returns the area of the circle (pi r2)
nameOfCircle.retCirc()
- Returns the circumference of the circle (2 pi r)
nameOfCircle.mvBy(xDis,yDis)
- Moves the circle by xDis in the x direction and yDis in the y direction
The following lines point the methods to functions that the object will use as the methods:
- this.retArea = getTheArea;
- this.mvBy = mvCclBy;
- this.retCirc = function () { ... };
The third of these defines the method using an anonymous function in one line, and does not work in some early Netscape 4 releases. Note, it has a semicolon after the '}', and is one of the few places where this is correct practice.
function mycircle(x,y,r) {
this.xcoord = x;
this.ycoord = y;
this.radius = r;
this.retArea = getTheArea;
this.retCirc = function ()
{ return ( Math.PI * this.radius * 2 ); };
this.mvBy = mvCclBy;
}
function getTheArea() {
return ( Math.PI * this.radius * this.radius );
}
function mvCclBy(xDis,yDis) {
this.xcoord += xDis;
this.ycoord += yDis;
}
var testcircle = new mycircle(3,4,5);
testcircle.mvBy(2,3);
window.alert( 'The area of the circle is ' + testcircle.retArea()
);
window.alert( 'The circumference is ' + testcircle.retCirc() );
The special 'toString' method
All objects have the
'toString' method, even if you do not define it yourself. The method returns a string representation of the object, and is automatically called whenever a string representation of an object is required, such as when you use
alert(myObject);
In most browsers, this returns
'[object Object]', although some more useful browsers return a string like this:
'{property1:value1,property2:value2,method1:function ()
{ ... },etc.}'
However, for your own objects, you may wish to provide a special string that may provide more useful information. To do this, simply define the
'toString' method:
this.toString = function () {
return 'Circle object; xcoord: ' + this.xcoord + ', ycoord: '+
this.ycoord + ', radius: ' + this.radius;
};
Advanced object techniques
These deal with concepts that are rarely used in JavaScripting. JavaScript is
a more powerful programming language than most people make use of,
but that is because in normal scripting, these powerful features are not really
necessary. If you are just learning
Some of these techniques may not work in older browsers like early Netscape
4 releases.
Adding extra properties/methods using prototype
Take the
mycircle example from the top of this section (creating new objects). We have already created an instance of the
mycircle class called
'testcircle'. And we can also assume that we have created a few other
mycircles. Now, let us assume that we want to add another property to each circle. For example, we want each circle to have a
'texture' property. We could use this:
testcircle.texture = 'smooth';
And we could do this for each individual
mycircle. But since we want to do this for all of them, it would be much more easy to give all instances the property at the same time. So we can use the
'prototype' property of the class constructor:
mycircle.prototype.texture = 'smooth';
Immediately, all of the mycircles that we have created will now
inherit the new property:
alert(testcircle.texture);
We can add new methods the same way:
mycircle.prototype.setArea = function (oArea) {
this.radius = Math.sqrt( oArea / Math.PI );
};
mycircle.setArea(5);
This is most useful with instrinsic (fundamental inbuilt) objects. For example,
the regular expression construct /a[0-9]b/g
is shorthand for new
RegExp('a[0-9]b','g');
and in fact the same is true for all intrinsic
object classes, such as String, Number and Boolean. So if, for example,
we wanted to create a new method on all strings called 'reverse' that
returned their contents in reverse order, we could do this:
String.prototype.reverse = function() {
for( var oStr = '', x = this.length - 1, oTmp;
oTmp = this.charAt(x); x-- ) {
oStr += oTmp;
}
return oStr;
};
The use of prototype could have been applied to create all of the methods of
the mycircle object, not just new ones. This gives a mixed response to
performance. It will not have to store individual copies of the methods for each
instance of the object, so it may require less memory, but it will require the
browser to search the current and parent scopes to find the methods. This may
cause a marginal delay. Generally, you should use what is appropriate for
your code, and not base this decision on performance (unless you are dealing
with a very specific controlled environment):
function mycircle(x,y,r) {
this.xcoord = x;
this.ycoord = y;
this.radius = r;
}
mycircle.prototype.retArea = function () {
return ( Math.PI * this.radius * this.radius );
};
mycircle.prototype.retCirc = function () {
return ( Math.PI * this.radius * 2 );
};
mycircle.prototype.mvBy = function (xDis,yDis) {
this.xcoord += xDis;
this.ycoord += yDis;
};
In some cases, it may be desirable to work out if a property on an object is
attached to the object instance itself, or somewhere in its prototype chain.
In JavaScript, all objects have a method called hasOwnProperty
that returns true if the given property is attached to the individual object
instance. Of course, it is also possible to check if the object's constructor
also has the same property with the same value as the object instance itself,
but that can give the wrong result if separate properties exists with the same
value on both the object instance and the prototype chain.
The hasOwnProperty method accepts a single parameter; the name of the
property as a string.
function myclass() {
this.propertytwo = 2;
this.propertythree = 3;
}
myclass.prototype.propertyone = 1;
myclass.prototype.propertytwo = 2;
var myinstance = new myclass();
myinstance.propertyfour = 4;
alert(myinstance.hasOwnProperty('propertyone'));
alert(myinstance.hasOwnProperty('propertytwo'));
alert(myinstance.hasOwnProperty('propertythree'));
alert(myinstance.hasOwnProperty('propertyfour'));
alert(myinstance.hasOwnProperty('propertyfive'));
Public and private properties
This is a concept that is almost never used in JavaScript, and there is a good reason. It simply is not necessary. Even complicated scripts are almost never complex enough to require this level of control. However, many programmers familiar with other programming languages (such as Java or C++) often desire this behavior in JavaScript simply because it is a concept they are familiar with. This sort of concept is only really useful when putting together large projects from multiple pieces of code.
Say for example that I am producing a public functions library. A set of constructors and methods that people can include in their own projects (something that Java programmers use all of the time). Say for example that the mycircle constructor is included in it, so that you can create your own mycircles. Now say for example you try this:
var aCircle = new mycircle(5,6,'about 3mm');
That will work now (it's not a valid measurement, but my constructor will not argue), but later on when you try to use the methods that my class provides, it will fail. So I can check what you have provided, and make sure it is a valid number, and use a default value if it is not. But then you can say something like this:
aCircle.radius = 'some text';
Again, it would break (of course, it is your own fault, but in more complicated
applications, it would be possible to make a mistake where this causes
a real problem). So what I want to do is to not allow you to modify that
property directly, and only allow you to change it using a method that I control:
this.setRadius = function (oRad) {
if( typeof(oRad) == 'number' && oRad >= 0 ) {
this.radius = oRad;
} else {
this.radius = 0;
}
};
An alternative situation where this can be important is if I am storing the information in one set of properties. I then upgrade my libraries, maybe adding some extra functionality. But in order to do so, I have to change the properties that I am using. If your script was relying on them existing in a specific format, and I have now changed that format, your script would fail. If I could protect those properties, and force you to use only methods, then I could do whatever I needed with the properties, and as long as I then change the methods to use the new properties, your script would keep working, and you would not even need to know what I had changed or why. That way, we could each manage our own project, and not waste each other's time.
It would also help avoid possible conflicts between undisclosed properties, and your own scripting. For example, if you decide to temporarily assign a property to an object, but you were unaware that internally, my object constructor already used that property name, you would overwrite my object's property, and cause problems with the object.
This is what private properties are for. They do not allow scripts that use the contructor to use or modify the properties directly. They only allow the methods of the object itself to use and modify them. Unlike many other languages, JavaScript does not declare each variable type as 'public' or 'private'. It all depends on how you create them.
Saying
'this.propertyname
' as I did above will create a public property. Any script can create an object then use and modify its properties directly. Using
'var' to define a variable in the constructor will create a private property. Note that unlike public properties, the private properties are then accessed, including from within methods, without the 'this.' prefix - just like a normal variable. This makes heavy use of JavaScript's scope functionality. Private variables can only be accessed from methods that are declared inline, and not externally referenced or created using the prototype construct. Methods of this kind are also known as privileged methods. An example of its use might be:
this.mymethod = function () { alert( propertyname ); };
function myob() {
this.property1 = 'value1';
var property2 = 'value2';
this.method1 = function () { alert( property2 ); };
}
var oneOb = new myob();
alert(oneOb.property1);
alert(oneOb.property2);
oneOb.method1();
Similarly, you can also create private methods. These are simply a function that
you create inside the constructor function. This may look confusing, but it works.
The private function can only be called by the constructor itself, or by methods
that are defined inline. Private methods can be used as public methods,
if they are assigned to a public method constructor, and accessed using
the public method constructor (as with 'method2' below).
function myob() {
function cantBeSeen() {
alert(secretValue);
}
var secretValue = '';
this.method1 = function () {
secretValue = 'no surprises';
cantBeSeen();
};
this.method2 = cantBeSeen;
}
var oneOb = new myob();
oneOb.method1();
oneOb.method2();
Sub-classes and class inheritance
This is also hardly ever used in JavaScript, despite its popularity in other languages. Take this as an example. I want to make a new type of object, that will store the data to represent a sphere (a ball - in case you don't know). Since a sphere is just a three dimensional circle, I would probably want to include all the methods I created earlier for the circle object;
retArea to get the cross-section area,
retCirc to get the circumference,
mvBy to move it by a certain amount. Aditionally, I would probably want
retVol to get the volume, and
retSurf to get the surface area. I would also need to provide a z coordinate when creating the object, and again when calling the
mvBy property.
So, I want to create a new type of object, based on the
mycircle, but with a few aditions and modifications. I will call this new type of object a
mysphere. Now, I could just rewrite all the code I already wrote for the
mycircle and change the bits I need. But in a real-world application, this might be a lot of wasteful duplication, for only one or two modifications. So what I want to do is make the
mysphere inherit all the properties and methods of the
mycircle, then overwrite only the ones I want.
This effectively makes
mysphere a sub-class of
mycircle, and making the
mysphere class constructor inherit from the
mycircle class constructor is as simple as this:
function mysphere(x,y,z,r) { ... constructor code ... }
mysphere.prototype = new mycircle();
In case you are wondering, the way it works is to actually create a mycircle,
then assign that to the mysphere constructor prototype. As a result,
the mysphere constructor has the mycircle object added to its prototype
chain. What this means is that each time a mysphere is created, it will inherit
the properties and methods of the mycircle object. It can then override any
of these properties using its own prototype, or with properties and methods
created within its own constructor. If any of these properties are subsequently
removed (using the 'delete' keyword), the inherited ones will become available
again. That also means that if the mycircle prototype is changed
(properties are added or deleted, etc.), these changes are replicated down
the chain, so the mysphere also inherits these changes.
Note that the prototype chain is established for each object as the object is created. Once an object has been created, its prototype chain cannot be changed (though new properties and methods can still be added to its parent classes, and those changes will be reflected in the object). Changes to its prototype chain will have no effect; it will still see the old prototype chain. The new chain will only be used for new instances of that class. This is also the case for the assignment of a new
mycircle to the prototype of
mysphere (which, as stated, creates a new instance of the
mycircle class); any subsequent changes to the prototype chain of the
mycircle class will not be reflected in the
mysphere class. For this reason, it is very important to ensure that the prototype chain is built in the correct order, with class constructors and their prototypes set up before any child class is made to inherit from them. Properties and methods can still be added to any class or parent class at any time after their own prototype chain has been set up.
The line that makes
mysphere inherit from
mycircle sounds simple enough. And at least, it would be, but you will remember that
mycircle expected me to pass it some parameters that it would then use. Then I call my new constructor, and these parameters will not automatically be passed to the
mycircle constructor function. I need to do this myself for each mysphere that I want to create. But since I am not passing these parameters when assigning the
mycircle object to the
mysphere prototype, I also need to ensure that the
mycircle contructor stops before attempting to create its own properties. Instead of defining the public properties immediately, I will define them using a method (that I have decided to call getready), and if the required parameters have been passed (
arguments.length), I will immediately call the method.
If the parameters are present, the
mycircle constructor keeps working, and no-one needs to know that anything was changed at all. To make things easier later on, I will take all of the methods out of the constructor, and add them later using the prototype. That way, the
mycircle prototype methods will always be available without me needing to create a new
mycircle. Unfortunately, this also means that public and private properties are very hard (or impossible) to use. It's a trade off - one functionality for another.
function mycircle(x,y,r) {
if( arguments.length ) { this.getready(x,y,r); }
}
mycircle.prototype.getready = function (a,b,c) {
this.xcoord = a;
this.ycoord = b;
this.radius = c;
};
mycircle.prototype.retArea = function () {
return ( Math.PI * this.radius * this.radius );
};
mycircle.prototype.retCirc = function () {
return ( Math.PI * this.radius * 2 );
};
mycircle.prototype.mvBy = function (xDis,yDis) {
this.xcoord += xDis; this.ycoord += yDis;
};
Now, back to the
mysphere constructor. I have already said how to make it inherit, but it still needs to run the
mycircle.getready method to initialise the properties. To do this, we will need to reference the parent class prototype, and run the method ourselves. This also ensures that even if each parent class uses the same method name
(getready), the correct one is always referenced. Since we can almost never be sure how many sub-classes we will need, it is useful to do it this way, and simply use the same method name for each, knowing that we will not have problems with name conflicts.
When running the method, we need to tell JavaScript that even though we are referencing a method for a different prototype, we want to run it as if it were a method of the object we are creating (to make sure that any properties it creates are added to the object we are creating). This could be done using the
'call' method or
'apply' method, but unfortunately, Internet Explorer 5 does not understand these, so I will assign it to a temporary property of the new object, and run it from there.
To reference the method from the parent class prototype, it would be possible to use the constructor's prototype chain to locate it (the property that references the constructor will, at this point, refer to the parent class - more on that later):
this.constructor.getready
However, that will only work for one level of inheritance, due to the way it is run as a method of the current object for all child classes. As a result, it is best to reference it by name, which will work no matter how many levels of class inheritance are used:
mycircle.prototype.getready
Note that when assigning the mycircle object to the mysphere prototype,
it also overwrites the mysphere prototype constructor property. This is
not a major problem, but some scripts use it, so we will put
the reference back where we want it:
function mysphere(x,y,z,r) {
if( arguments.length ) { this.getready(x,y,z,r); }
}
mysphere.prototype = new mycircle();
mysphere.prototype.constructor = mysphere;
mysphere.prototype.getready = function (a,b,c,d) {
this.tempReady = mycircle.prototype.getready;
this.tempReady(a,b,d);
this.zcoord = c;
}
mysphere.prototype.mvBy = function (xDis,yDis,zDis) {
this.xcoord += xDis;
this.ycoord += yDis;
this.zcoord += zDis;
};
mysphere.prototype.retVol = function () {
return ( 4 / 3 ) * Math.PI * Math.pow( this.radius, 3 );
};
mysphere.prototype.retSurf = function () {
return 4 * Math.PI * this.radius * this.radius;
};
And finally, to use it:
var testsphere = new mysphere(3,4,5,6);
alert( 'The cross-section area is ' + testsphere.retArea() );
alert( 'The circumference is ' + testsphere.retCirc() );
alert( 'The volume is ' + testsphere.retVol() );
alert( 'The surface area is ' + testsphere.retSurf() );
There is no limit to how many sub-classes can inherit from a class,
and there is also no limit to how many levels of sub-classes can
be created. As an example, I will create a ball class that will
inherit from the mysphere class:
function ball(x,y,z,r,m) {
if( arguments.length ) { this.getready(x,y,z,r,m); }
}
ball.prototype = new mysphere();
ball.prototype.constructor = ball;
ball.prototype.getready = function (a,b,c,d,e) {
this.tempReady = mysphere.prototype.getready;
this.tempReady(a,b,c,d);
this.mass = e;
}
ball.prototype.retDensity = function () {
return this.mass / this.retVol();
};
The instanceof operator returns true when testing an object against any class
in its prototype chain, so it would say that an instance of the ball class was
also an instance of the mysphere class, and also the mycircle class.