Tuesday, February 23, 2010

JavaScript Creating objects

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 next line uses an alternative syntax
  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;
}

/*
create a mycircle called testcircle where testcircle.xcoord is 3
and testcircle.ycoord is 4 and testcircle.radius is 5
*/
var testcircle = new mycircle(3,4,5);
/*
use the mvBy method to displace the centre of testcircle.
move it by 2 in the x direction and 3 in the y direction
*/
testcircle.mvBy(2,3);
//testcircle.xcoord is now 5 and testcircle.ycoord is now 7

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);
//alerts 'smooth'
 
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'));
//alerts false

alert(myinstance.hasOwnProperty('propertytwo'));
//alerts true

alert(myinstance.hasOwnProperty('propertythree'));
//alerts true

alert(myinstance.hasOwnProperty('propertyfour'));
//alerts true

alert(myinstance.hasOwnProperty('propertyfive'));
//alerts false 

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'; //this creates a public property
  var property2 = 'value2';  //this creates a private property
  this.method1 = function () { alert( property2 ); };
}
var oneOb = new myob();
alert(oneOb.property1); //alerts 'value1'
alert(oneOb.property2); //alerts undefined (private property)
oneOb.method1();        //alerts 'value2'
 
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(); //alerts 'no surprises'
oneOb.method2(); //alerts 'no surprises' 

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); }
}
//inherit from the mycircle prototype
mysphere.prototype = new mycircle();
//put the correct constructor reference back (not essential)
mysphere.prototype.constructor = mysphere;

mysphere.prototype.getready = function (a,b,c,d) {
  //reference the getready method from the parent class
  this.tempReady = mycircle.prototype.getready;
  //and run it as if it were part of this object
  this.tempReady(a,b,d);
  //now that all required properties have been inherited
  //from the parent class, define extra ones from this class
  this.zcoord = c;
}
mysphere.prototype.mvBy = function (xDis,yDis,zDis) {
  //override the existing method
  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. 




 

0 komentar: