FANDOM


PrologueEdit

JavaScript is an object oriented language, with very good procedural support as well.* However, many don't take full advantage of JavaScript's OO nature.

* If you don't know the terms, that's okay, but it might be worth it to look them up at some point.

If you don't have an instance of Y!WE handy, all of these examples will run in a Firefox browser, but the print statement will not exist. Instead you can use alert(*);. If you have the "Firebug" add-on installed (highly recommend it) you can use console.log(*); instead, and it will print out to Firebug's console, which is much nicer to work with than alert dialogs.

Getting StartedEdit

First it is helpful to know that in JavaScript, unlike some other languages, Classes are the same as Functions. You define them basically the same, but how they operate is totally different. When you call a function, it may take some parameters and may return a value. This is pretty straight forward:

function multiply(a, b) {
	return a * b;
}
print(multiply(3, 2));


First, some terminology:
scope Scope refers to the "level" at which you can see a variable. An example would be an office building floor with a bunch of offices. You can see and interact with the things in your office, you can see other offices (but not what's inside them), and you can ask coworkers to interact with the things in their office for you (via phone or e-mail, etc). If something is sitting outside an office (the floor's copier), anyone can access it.


If you create any variables that are scoped to that function, they will be destroyed (lost) when the function ends, like so:

function max(a, b, c) {
	var temp = Math.max(a, b);
	return Math.max(temp, c);
}
print(max(1, 2, 3));
print(temp);         // error, temp doesn't exist.

Math.max accepts more than two values, but for the purpose of this example, we assume it takes two, so we can assign the result of the first operation to a temporary variable. Since we defined that variable with the "var" keyword, it will be scoped to the function. That means that when we call the function, it will be destroyed when the function returns. Therefore if we try to access it on the outside, we will find it doesn't exist. This is a useful concept to us when we work with Classes.


First, some terminology:
instance When you do "new Class()" you are creating an instance of the class. They are also known as "objects". This is a live interactive object that allows you work with the stuff the class defined.
reference A "reference" is a way to get at an object (it points you to it). When you do var a = new Class();, you have a reference to an instance of Class inside of the variable a. You can assign this reference to more variables, and they will all interact with the same instance you created when you initially assigned it to the variable a.


Classes differ from the above functions in that the scope* defined when you create an instance is trapped inside that object until it dies. An object's "death" in JavaScript happens when nobody holds a reference to that object any more (and no one can get a reference to it). It is then considered "garbage", and periodically the aptly named "garbage collector" will come through and remove those dead objects. There exists a small issue of what to do when an object holds a reference to itself, but for all intents and purposes, we don't need to worry about that: they will be collected also. The advantages of this process are that we don't need to worry about every little possible advantage something might have (uses slightly less memory, etc) as memory is manged for us. This frees us up to worry about larger things, such as the efficiency of the algorithm or design we choose.

* This may also be referred to as the "environment" inside an object later in this article.

Here's an example of a simple class, with a simple environment:

function Class() {
	var number = 3;
}

We can't do much with that environment, but it still exists. If we do var a = new Class(); we have an instance of Class, but doesn't do us much good, really. It's like having only one LEGO block.

More of the BasicsEdit

Alright, so up to this point we've learned how to create a simple class. That class isn't worth much, but it's still a class and that's something. Let's work on some more advanced things.


First, some terminology:
private In some languages, you can mark variables as "private" inside of classes. This means that no one on the outside can access the contents of that variable unless a way is provided to do so. In JavaScript, there is no private modifier on variables, but variables can be trapped inside of a scope such that nobody outside of that scope can get at it. This is similar to the temp variable in the max example, and the number variable in the Class example.


The previous class didn't take any parameters. If classes are essentially functions, can't they take parameters too? The answer is yes! Also like functions, parameters given to a class when it is created are kept around for the duration of the life of the object. They are essentially private variables. This is just as simple as regular functions and works like so:

function Class(param) {
	var number = 3;
}

We don't do anything with the variable "param" but we can access it from anywhere inside the the scope of a Class object, and set it without fear of it being seen publicly (as happens when we declare a variable without the "var" keyword in other circumstances). This will become more important later, when we cover more things you can do within this environment.

ThisEdit

One important difference between functions and classes is that instance of classes have a special keyword (or variable if you prefer) called "this" that allows us to access things attached to the object itself. These are properties or functions which are publicly visible on both sides (inside the scope of the object and from the outside).

A simple example would be if we changed how we assigned our previous "number" variable:

function Class(param) {
	this.number = param;
}

We can now do the following:

var c = new Class(3);
print(c.number);

c.number = 4;
print(c.number);

This allows properties to be gotten and changed from either side on an object. Some problems exist with this model, which is that the outside (anything not inside the scope of the class instance) has unrestricted access to the property "number". They can even set it to a non-number, which is a problem given that we named that property assuming it would always hold a number. Not so! So what can we do to prevent this? We can certainly introduce some ways to make access less direct. There are several ways to do this.

First, we must ensure that incoming parameters are numbers:

function Class(param) {
	this.number = Number(param);
}

This results in "number" holding an NaN value if it is passed something other than a number or numeric string. If we're okay with that, we can leave it as such. If it's critical that it be a Number, we can tell the person creating our class we're not okay with it being a non-number. That's easily done as such:

function Class(param) {
	if(isNaN(Number(param))) {
		throw "Class expects a number for the first parameter";
	}
	this.number = Number(param);
}

This approach throws an error, stopping execution of code, if we don't receive something we like. Note that we still have to call Number(param) even after we've verified that it's a number. This is because that values like "3" will pass our check just as well as 3 will, and we want it to be a true number.

This still doesn't affect a person's ability to change it to a non-number later, so let's fix that. There's two good approaches for this:

Method (function) approachEdit

function Class(param) {
	var number = 0;

	this.getNumber = function() {
		return number;
	};
	this.setNumber = function(n) {
		if(isNaN(Number(n))) {
			throw "Class.setNumber expects a number";
		}
		number = Number(n);
	};
	this.setNumber(param);
}

Note that "number" is now private again. We can also now combine our code, so we only do the checking in one place. This has a slightly confusing issue of throwing an exception that doesn't quite describe the issue as well as it did before when we create an object. Therefore, you might want to preform the check twice, throwing a different error instead for the other case.

If you want the parameter to be optional, just add a check to the place where you set it:

	if(param !== undefined) {
		this.setNumber(param);
	}

If the variable isn't sent (the class is created with no parameters) it will now be ignored.

We can now be safe in knowing that number will always contain a number:

var c = new Class(3);
c.setNumber(4);
print(c.getNumber());

c.setNumber("three"); // error!

Something to note here, is that we now have separate ways to get and set the property "number" which is in stark contrast to how much of JS code is written. The next approach is a bit better in that regard, but does things differently.

Getter/Setter approachEdit

function Class(param) {
	var number = 0;

	this.__defineGetter__("number", function() {
		return number;
	});
	this.__defineSetter__("number", function(n) {
		if(!isNaN(Number(n))) {
			number = Number(n);
		}
		return n;
	});
	this.number = param;
}

This has the advantage of looking like a public property to code outside our class, and is more "JS-like" in how it operates. However, you may have noticed there are some differences. The main difference is now that we are not warned of bad values, they are just ignored. You can throw an exception from a property, JS will not prevent you, however, it is generally agreed* that properties do not throw exceptions, but functions can. If you abide by this, that is one reason to use a function instead of a property, if it is absolutely necessary to tell the outside that they screwed up.

* If I'm wrong, or people disagree, feel free to edit and/or reword.

The Good, the Bad, and the UglyEdit

As the person writing the tutorial, I'll say this outright, I prefer the second method when working with JavaScript. It's more "native", and more like what the "natives" do (When in Rome ...). People expect that over the function approach, especially if they've come from languages similar to JavaScript. If you came from a Java background, you might be more familiar/at-home with the first approach, and that's ok as long as you're consistent through-out your code.

The GoodEdit

Both of these approaches allow protected access to your inner variables, and allow you to do various things with them before you actually set them. You can even do multiple things inside each because they're functions, and not just a property hanging out there. Both approaches also let you make variables read-only or set-only, just omit the appropriate getter or setter definition.

The BadEdit

Both approaches require more code than just a simple property, but for the benefits they provide, the advantages outweigh this extra bit of typing. Functions can also take more than one parameter, where as properties (being singular by nature) cannot. This means that if you want/need to take more values, you either need to use a function instead or combine them into another object which makes sense for those parameters. An example would be to use a Point object property instead of passing x and y variables to a function. Please don't make users group relatively unrelated things into an object (such as an array) just so you can use a property.

You may have noticed that in the JS-getter/setter approach, I returned the value which was sent in. This is in line with JS convention. It allows programmers to "chain" statements, like so:

var c = new Class(); // using JS-get/set code
print(c.number = 3);

Using the function example, that would print "" or undefined. However because we return a value, we can print it, store it in another variable, pass it to another function, or just plain ignore it.

Since we return the original variable we passed in, there's no indication (especially not for set-only variables) whether we succeeded or not without re-reading the property to see if it changed. However, most times this won't be too much of a burden. The programmer of the class just has to remember to return the variable.

One last bad thing relates to the getter/setter approach. This is the wiki for Konfabulator, but it is worth mentioning. Getters and setters as shown are a Mozilla extension, and are only available in JS environments which provide compatibility with this extension. So if you were to run some JS code using these inside of Internet Explorer, they wouldn't work and may be seen as a compile error.

The UglyEdit

Well, it's debatable what goes here instead of "The Bad", but there are some things.


First, some terminology:
pass-by-value In most languages, when you pass a variable to a method or function it passes the value of what you are trying to give to the function. When primitives are involved, this works quite well. If you pass a variable containing 3, the function gets a copy of the value 3. An example:
var a = 3;
function mutate(b) {
	b = b - 1;
}
mutate(a);
print(a); // a is still 3
It becomes muddled when you involve variables with objects, because they are really a reference to the object, and when you pass it to a function, it copies the reference, and not the object itself.


When someone passes an object to a function or a getter/setter, because of how pass-by-value works, if you store the variable directly, they are able to modify that variable from the outside, and without your knowledge. So if we were to take the simple example of an array, we'd end up with something like the following:

function Class(array) {
	var list = array;

	this.print = function() {
		print(list);
	};
}

var a = [1, 2, 3];
var c = new Class(a);
c.print();

a[2] = 4;
c.print();

In that example, we were able to successfully able to change a value of the array we passed to the class because an array is an object. In this example, a way to partially prevent this would be to have JS copy the array for us:

function Class(array) {
	var list = array.slice(0); // copy the array from index 0
}

Now, if you noticed I said partially, you're observant. I say partially, because with arrays, copying the array prevents direct modification to the structure of the array and its contents, but only if it contains primitives. If it contains other objects, we're not entirely out of hot water.

This leads us to the reason why I listed this under "The Ugly": Since it's not practical to copy every thing we receive, and because some objects may not have a defined copy mechanism, we just have to do what we can, and hope the code on the outside is as well behaved as we are.

Intermediate BitsEdit

Useful FunctionsEdit

toStringEdit

Try printing out an instance of our previous example class, and you'll probably get a result that looks like this "[object Object]". We can easily remedy that by defining the toString function, as follows:

function Class() {
	this.toString = function() {
		return "[object Class]";
	}
}

We can return any string value we like, even ones that are more descriptive of the contents or state of our object. This allows us to identify an object type easily or take a look at a state of an object with minimal work, if we choose. Unfortunately, Y!WE just implements toString functions similar to the one described above, so we don't learn much about the state of an Image object, for example. The nice thing about toString functions is that when printing things, most times we don't even have to call it ourselves directly.

Example:

var c = new Class();
print(c);
print(c.toString()); // same output

valueOfEdit

What if we could get basic operators such as !==, >, ===, etc. to work the way we want to with our objects, even though such comparison might not normally make sense? First an example:

function Letter(value) {
}

var one = new Letter("A");
var two = new Letter("Z");

if(one > two) {
	print("1 > 2");
} else if(one < two) {
	print("1 < 2");
}

Given that example, we shouldn't see any output. That is because there is no valid comparison for > or < for those to objects. Rather than invent one, JS just says that the evaluations of both expressions will always return false.

In steps the valueOf function. Normally, it returns the instance we called it from, but we can't really tell, because we didn't define a toString function, like so:

print(one.valueOf());
print(two.valueOf());

Now, this is where it gets a bit strange. If we do define a toString function, and run that code again:

function Letter(value) {
	this.toString = function() {
		return "[" + value + "]";
	};
}

var one = new Letter("A");
var two = new Letter("Z");

if(one > two) {
	print("1 > 2");
} else if(one < two) {
	print("1 < 2");
}

The results are different! It now prints "1 < 2" because the string "[A]" is alphabetically less than "[B]". However this isn't entirely ideal, as it's now picking a way to evaluate our objects for us. What if we want to define our own?

Let's use the valueOf function:

function Letter(value, value2) {

	this.valueOf = function() {
		return value2;
	};

	this.toString = function() {
		return "[" + value + "]";
	};
}

var one = new Letter("A", 2);
var two = new Letter("Z", 4);

if(one > two) {
	print("1 > 2");
} else if(one < two) {
	print("1 < 2");
}

In this example, it will print "1 < 2", because the valueOf function takes precedence over the toString function. It should be noted here that the valueOf function should always return a primitive value (number, boolean, ...) in preference to something else to ensure the outcome isn't unexpected.

Alright, we can do < and > comparisons, but what about the === and == promised above? Well, sorry to stint, but JS doesn't let us override the behavior of ===, ==, !==, !=, &&, or || using the valueOf function. They always work same as before.* However, operators such as ^, & |, and % do obey the results of this function.

* I feel they should at least affect && and ||, but that would also require changing the rest to match ...

SummaryEdit

The author's consensus is while the valueOf function might be useful in some cases, it is probably best to avoid the "initial infatuation". This means that while it's neat (cool) you shouldn't try to find little creative uses for which to abuse it. Just a cautionary suggestion.

Functions Inside ClassesEdit

We didn't talk about this earlier because it would have not quite fit, and be generally more confusing.

Earlier, we were able to have private variables containing primitives and objects inside an object. However, you can define functions in that scope as well, and they will only be visible inside an instance of that class, like this:

function Class() {

	function multiply(a, b) {
		return a * b;
	}

	this.multiply = function(a, b, c) {
		return multiply(multiply(a, b), c);
	};
}

This example is rather silly, but it illustrates the point that it is possible.

This simple example works, but let's now define a toString (so we can later identify our own class), and change the inner function to use a different source for its values:

function Class() {

	var a = 2;
	var b = 3;

	function multiply() {
		return a * b;
	}

	this.multiply = function() {
		return multiply();
	};

	this.toString = function() {
		return "[object Class]";
	};
}

var c = new Class();
print(c.multiply());

So far so good, right? It should print a value of 6.

If we change the source of our variables once again:

function Class() {

	this.a = 2;
	this.b = 3;

	function multiply() {
		return this.a * this.b;
	}

	this.multiply = function() {
		return multiply();
	};

	this.toString = function() {
		return "[object Class]";
	};
}

var c = new Class();
print(c.multiply());

Running this example will result in an interesting conclusion. It now prints "1.#QNAN" or just "NAN". Why is this? "this" should point to the enclosing instance, right?

The short answer to these questions is nope, it doesn't. If you print "this" inside of that function, it'll actually print "[object Global]" ... oops!

function Class() {

	this.a = 2;
	this.b = 3;

	function multiply() {
		print(this);
		return this.a * this.b;
	}

	this.multiply = function() {
		print(this);
		return multiply();
	};

	this.toString = function() {
		return "[object Class]";
	};
}

var c = new Class();
print(c.multiply());

The function multiply is still local to our instance of Class (no one else can see it), but its scope is that of global. There are at least 4 ways to fix it. They all have different applications, and I'll cover them briefly, but if you want more details, please visit the JavaScript section of the Mozilla Developer Center (MDC).

Reference To SelfEdit

The easiest approach is to keep a reference around to yourself. Many developers do this with a variable defined just inside the top of a class. Names range from "that", "$this", or "outer", etc. This is the author of this article's favored approach.

It works like this:

function Class() {
	var $this = this;

	this.a = 2;
	this.b = 3;

	function multiply() {
		return $this.a * $this.b;
	}
}

If we were to call multiply from inside the object, it would return the previously correct value of 6.

Call / ApplyEdit

Another approach which allows for other things is using the call or apply functions of a given function.

function Class() {

	this.a = 2;
	this.b = 3;

	function multiply() {
		return this.a * this.b;
	}

	print(multiply.call(this));
	print(multiply.apply(this));
}

var c = new Class();

For the purpose of this example, call and apply work the same. They both call the function multiply from the scope of the given object, in this case the outer class instance. They differer for other things outside of this example, but as said above, please visit the MDC for details.

Watching Public PropertiesEdit

It can be very useful to watch public properties for changes. This allows people using a given object to be notified if someone/something makes changes to it.

This only works for those that chose to use the JS-getters/setters approach to allowing access to inner variables. If functions were used, it is impossible to watch for changes in values, because we only end up watching the function itself, not what it returns.

Very simply, we can set up a watch statement like so:

function Class() {
	this.number = 3;
}

var c = new Class();
c.watch("number", function(property, oldValue, newValue) {
	print("number was " + oldValue + " and now is " + newValue);

	return newValue;
});
c.number = 4;

The first parameter we provide to the watch statement is the name of the property we are watching. The function we provide will be called when the value is changed. It is passed the name of the property it is being called for, the old value of the property, and the new value. Watch statements must always return a value.* This is because they are called before the value is set.** This can present an issue that any functions or code called by the watch statement will not get the latest value of the property if they ask the object itself.

* If you set the watch function to always return the old value, it makes the property essentially read-only.
** This is where this article's author lodges his official complaint: if it's a watch statement, returning a value from it should not affect the value of the property. It should only be used for watching. Anyways, I digress ...


Example:

var c = new Class();
c.watch("number", function(property, oldValue, newValue) {
	myPrint();

	return newValue;
});
c.number = 4;

function myPrint() {
	print(c.number);
}

This example will print 3, not 4. A solution to this problem is to have code that relies on this value get passed the new value from the watch statement, like this:

c.watch("number", function(property, oldValue, newValue) {
	myPrint(newValue);

	return newValue;
});

function myPrint(number) {
	print(number);
}

PitfallsEdit

There are some traps and pitfalls to watching properties. The main one is that if you watch a property on a built-in Y!WE object, you will only be notified of changes your own code made to those properties. This just isn't all that useful in most cases, as Frames and Windows have sizes that can vary with their contents.

Another problem is that it is very easy to fall into this trap when designing a class. The private variables are easily accessible, and setting them is quick. However, this is a thing to avoid, as those watching your variables on the outside get no notice.

Here's a quick example:

function Class() {

	var number = 3;

	this.__defineGetter__("number", function() {
		return number;
	});
	this.__defineSetter__("number", function(value) {
		if(!isNaN(Number(value))) {
			number = Number(value);
		}
		return value;
	});

	this.increment = function() {
		number++;
	};
}

var c = new Class();
c.watch("number", function(property, oldValue, newValue) {
	print("number was " + oldValue + " and now is " + newValue);

	return newValue;
});
c.increment();

If we run this, we'll get no notification that it ever changed. How nice of it!

If we change how the increment function works just slightly:

	this.increment = function() {
		this.number++;
	};

We not only tell anyone watching the value changed, we also send it through our own code, which verifies that it's a good value to accept! (doesn't seem like much for a number, but this still applies to more complex things)

Advanced BitsEdit

PrototypeEdit

JavaScript's object oriented nature is built upon the idea of the prototype property. This public property allows developers to extend a given class by adding functions to it from the outside. This has the advantage of allowing us to extend built in classes, classes we didn't write, or even our own. Previous to this, we only defined our functions inside of the class, like this:

function Class() {

	this.multiply = function() {
	};

	this.toString = function() {
		return "[object Class]";
	};
}

These functions are then added to an instance of Class when it is created.

However, we could also write it as such:

function Class() {
}

Class.prototype.multiply = function() {
};

Class.prototype.toString = function() {
	return "[object Class]";
};

For basic things such as this, there is no real difference between the two approaches except for the way they are written.

Following that example, we can extend Y!WE's built in Point object to have the proper toString function it deserves:

Point.prototype.toString = function() {
	return "Point(" + this.x + ", " + this.y + ")";
};

var p = new Point(5,7);
print(p);

This ensures that every instance of Point we make will have a toString that prints out its location.

However, we can quickly run into issues if we start involving private variables. If we have private variables in the base class we are extending, prototyped methods cannot see those variables. They can only do with publicly available information:

function Class() {
	var number = 3;
}

Class.prototype.multiply = function() {
	return number * number; // will error when run!
};


Despite this, using the prototype does have some advantages. When you add items to a class via the prototype, only one instance of the function is created, and it is shared between all instances of the class to which it was added. This is in contrast to the earlier approach where all functions defined inside of the class are created at the time someone creates an instance of the class.

The fact that the functions are shared means that it will take less time to create a new instance of that class. However, this shouldn't enter into your decision to use prototyping versus the inner approach at all. This is only really a concern if your code is not running very fast, and you profile it (also available through the Firefox Firebug add-on!) to find that creating instances of a class is where the majority of time is spent. Places where object creation time might be important are in algorithms, functions which are called on a mouse drag, etc.*

* Trying to find these tiny optimizations before you run your code or before it becomes evident there is a problem is something to avoid. Some programmers call it "premature optimization" or more simply "a sin", either way, problems such as this should only be addressed once they've been proven to be a problem.

Class InheritanceEdit

What's the quickest way to get rich in Object Oriented programming? Inheritance! ... Okay, bad joke, just had to include it though.


Inheritance is the idea that a Class can inherit, or get functions/properties, from another class, referred to in this case as a "parent class". This is an alternative to the prototyping mentioned above, but it is not the same as prototyping. It allows you to have a class that obeys the same scoping rules as we've worked with for this document.

In JavaScript, if a class extends another class, scoping rules still hold, and the subclass (class doing the extending) cannot access variables private to the parent class. The functions gained from the parent class can see those variables however, nothing has changed for that class. It should behave exactly the same.

Prototype-based InheritanceEdit

The syntax for extending a class is as follows:

Class.prototype = new ParentClass;

This has to be called after both Class and ParentClass have been defined.

Let's give an example:

function ParentClass() {
	var a = 3;
	var b = 2;

	this.multiply = function() {
		return a * b;
	};
}

function Class() {
	this.double = function() {
		return this.multiply() + this.multiply();
	};
}
Class.prototype = new ParentClass();

var c = new Class();
print(c.multiply());
print(c.double());

As I noted above, the class "Class" cannot see any private variables of the parent class, only those defined with in its own scope (none at the moment). It can however, call functions on the parent class and access public properties.

A disadvantage of this approach is that parameters can be passed to the to the parent class only when it is initialized. Another side effect of this method is that since the prototype line is only called once, each subclass shares the same instance of the parent class! If you don't believe me, run the following example:

function ParentClass() {
	var variable = "hello";

	this.setItem = function(value) {
		variable = value;
	};

	this.print = function() {
		print(variable);
	};
}

function Class() {
	this.setVariable = function(value) {
		this.setItem(value);
	};
}
Class.prototype = new ParentClass();

var c = new Class();
c.print();

var d = new Class();
d.print();

d.setVariable("ehlo");
c.print();             // The value of c's variable changed!

The example is very simplistic, but it illustrates the point well. Other approaches to inheritance avoid this, but they have their own issues.

Instance OfEdit

Alright, how do we know if an object is of type A, B, ParentClass, Class ... ? Well, that's easy. There's a operator built into JS to tell us exactly that, called instanceof. As its name implies, it checks to see if the value to the left of it is an instance of the value to the right of it. The left value must be an object, the right value must be a Function/Class.

Using the above:

print(c instanceof Class);

Should print true, as "c" is indeed an instance of Class. However, this doesn't always work, but that's covered below.

Call-based inheritanceEdit

It is also possible to dynamically inherit from another class, without using the prototype method. This avoids avoids the issue where you share one instance of the parent class between all subclass instances, as you create a new instance of the parent class each time the subclass is created.

This works like this:

function ParentClass() {
	var a = 3;
	var b = 2;

	this.multiply = function() {
		return a * b;
	};
}

function Class() {
	ParentClass.call(this);

	this.double = function() {
		return this.multiply() + this.multiply();
	};
}

var c = new Class();
print(c.multiply());
print(c.double());

This approach makes it possible to provide different parameters to the class each time you make an instance, like so:

function ParentClass(a, b) {

	this.multiply = function() {
		return a * b;
	};
}

function Class(a, b) {
	ParentClass.call(this, a, b + 1);

	this.double = function() {
		return this.multiply() + this.multiply();
	};
}

var c = new Class(2, 2);
print(c.double());

var d = new Class(1, 2);
print(d.double());

The call approach has the problem that subclasses to not inherit anything added directly to the prototype of the parent class, like so:

ParentClass.prototype.toXML = function() {
	return "<parentClass/>";
};

If we were to try to call it on the subclass (given we added that code to the initial example), it'd error out and tell us that function doesn't exist. When we "call" the parent class, we only add anything defined via the inner syntax to our subclasses list of functions and properties.

We find another problem if we try to ascertain that "c" is an instance of ParentClass, as our previous champion operator "instanceof" will not work.

Try it:

print(c instanceof ParentClass);

It will always print false. This is because the prototype is not an instance of the parent class.

Using BothEdit

We can fix the issues of both previous approaches by using using them together, something this article's author recommends. It allows us to say that we're an instance of the parent class, but also avoid the issue of sharing, and also allows us to specify different parameters to the parent class when we create a new instance of the subclass.

Writing a Good ClassEdit

This is just an amalgamation of all the previous tips, plus some.

When creating a class, you should always try to make variables/data private (within reason) that you do not want outside code to have access to, even if you're the one writing the outside code. It's no reason to get sloppy just because you're in control of everything. The reverse is also true, your class should not rely on things specific to the outside environment, as this reduces portability. Portability doesn't mean portable to another language, it means portability between other projects. It should be very easy to redeploy your own classes in a new project.

Classes should check values being passed to their properties or functions to ensure they are the proper type they expect. Also be aware that when objects are passed into your class, they could have their own properties or functions which can still be called by anyone else holding a reference to it. If you put something out for complete public access, be aware outside code can do anything with it. Remember that watched properties are not updated until after the watch ends, not returning a value can cause trouble.

Keep in mind that inheritance should only be applied to similar things. "It saves code" is not a good reason for class to inherit from another class. It should be logical that a class is an extension of some class, not require lengthy explanations for your choice.

Classes should always be named appropriately for what they do. You shouldn't name a class "Blue" if it can be used to create many other colors ("Color" would be a better choice here). Class names are (by convention) singular, so you might name a class Color, but not Colors.

The final tip is more of opinion/style than anything, but if it makes your code cleaner, it's okay to define one class per JS file. It can be hard to read and manage code if it's all in one file (as is some people's preference).

These are not hard and fast rules, they are guidelines suggestions. They're to help build good programming practices that allow you to write things that are easily read or used by others and yourself. As you get better, you will start to form your own practices.

EpilogueEdit

I hope that all readers of this tutorial got something out of it. That means I would like to think that even if you were already well versed in JavaScript, that you found something you didn't know or an alternate approach.

I invite those knowledgeable in the subject to fix any errors in the code I posted or correct any other wrong material.

I'd like to thank Fx2 (yeah, I still use it) for helping not make spelling errors, and The Libertines for the wonderful musical entertainment while I wrote this article. I don't want to thank Wikia for having a poor wiki parser.

Thanks, and have a nice day!

- Art

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.