Trying out prototyping

Below is my stream-of-consciousness attempt to figure out Javascript's prototyping and inheritance, prior to embarking on some Javascript projects. I may be totally off-base. (TOOTBLAN?)

Constructor functions

A constructor function is a regular function, but has magic properties when invoked with new. E.g.,
F = function(args) {
	Do something;
	return results;
}
is any function, and g = F(args) just means g = results. But consider
g = new F(args);
The new (sort of) changes F into a new function, say newF:
newF = function(args,this) {
	Do something;
	return this;
}
where this is a new object created by the call. Note the results don't get passed to g, but the object this does. Thus the 'doing' is useful if is does things to this, e.g., add members:
F = function(i) {  
	this.x = 99;
	this.y = i;  
	return i*i;
}
so that
newF = function(i,this) {  
	this.x = 99;
	this.y = i; 
	return this;
}
Then g = new F(3) yields an object with two components:
g.x --> 99
g.y --> 3

Prototypes

Crockford's explanation of prototypal inheritance. Any object can have a prototype member just by creating it, e.g., g.prototype = 33. But it isn't anything special. A function, though, has an automatically created prototype member, one of whose further members is constructor, which is just the function itself. For the F above,
F.prototype.constructor --> function F(i) { this.x = 99; this.y = i; return(i*i); }
With g = new F(3), g gets assigned F.prototype as its prototype, which means the members of F.prototype are (overridable) members of g, though not members of g.prototype:
g.prototype --> undefined
g.constructor -->  function F(i) { this.x = 99; this.y = i; return(i*i); }
g.x --> 99
g.y --> 3
Now F can add members to its prototype, and these will carry over to g's prototype:
F.prototype = {b : 9, c : 17};
g = new F(3);

g.b -->  9
g.c -->  17
[This time, g.constructor --> function Object() { [native code] }.(‽))] The cool thing is now g is known to be an F:
g instanceof F  -->  true
Note: It doesn't seem to make sense to have a function be used both as a regular function and as a constructor. Calling the straight F(8) will still have to deal with the this object. Since it's not being passed to the function, it is a global this, which is the window object:
window.x and window.y  -->  undefined   undefined  
F(8) -->  64 
window.x and window.y  -->  99  8
Moral: A function should either be used the regular way, or be newed. Not both.

How to handle creating objects

I have to figure out how I'm going to create objects of a particular type. Some criteria for the approach:
  1. Supports inheritance (probably just single?).
  2. instanceof works, for both a class and any class it's derived from.
  3. The template (constructor, factory, whatever) is just one object/function.
  4. It is easy to write the template.
  5. It is easy to instantiate objects; In particular, all such functions should either be newed or all should not be newed.
I can't see how to get 2 and 3 to work together. The best I can think of so far is to have one big function, then an extra statement outside of the function. I have two simple approaches, depending on whether to new or not.

Using a constructor

The constructor, for the class NewClass, say, is the function NewClass(args), which constructs the this. So it's called using nc = new NewClass(args). It can do everything within that function but set the prototype. So, if baseobject is an object, we have the extra statement, outside the function, of NewClass.prototype = baseobject. For example, if the base object is
baseobject = {a: 7, b: 8, c: function() {return(this.a+this.b)}}
then we'd have the template
--- Template ---

function NewClass(i) {
	this.d = 3;
	this.a = i;
}

NewClass.prototype = baseobject;

--- /Template ---
Then instantiation and check:
nc = new NewClass(88): nc.a --> 88, nc.b --> 8, nc.c() --> 96, nc.d --> 3

(nc instanceof NewClass) --> true

Using a factory function

Now the whole construction is done in a function, but we have to have a blank constructor function defined outside of the factory function, in order for the class's name to be global (hence show up when testing instanceof).
--- Template ---

function NewClass(){};

function createNewClass(i) {
	NewClass.prototype = baseobject;
	var f = new NewClass();
	f.d = 3;
	if(i) f.a = i;
	return(f);
}

--- /Template ---
Then instantiation and check:
nc = createNewClass(88): nc.a --> 88, nc.b --> 8, nc.c() --> 96, nc.d --> 3 

(nc instanceof NewClass) --> true

It looks like the first way is a little easier. You don't need to remember to do as much: Just provide a constructor, and if needed set the prototype.

Chain of classes

Now to derive a class from NewClass (single inheritance), we set the derived class's prototype to be an object of the type NewClass:
function DerivedClass(j) {
	this.b = j;
}

DerivedClass.prototype = new NewClass(17);

dc = new DerivedClass(13): dc.a --> 17, dc.b --> 13, dc.c() --> 30, dc.d --> 3
This new derived class instance is both a NewClass and a DerivedClass, as desired:
(dc instanceof DerivedClass) --> true

(dc instanceof NewClass) --> true
That seemed to work ok. I wonder if I can call the NewClass constructor from the DerivedClass. Yes. Use NewClass.call(this,args).

Be careful with arrays in the prototype

Here's an example:
	function Simple(str) {
		if(str!=undefined) {this.x[0] = str;this.y = 4;}
	}
	
	Simple.prototype = {x:[9], y:3};
	
	var t = new Simple(): t.x[0] -->9, t.y --> 3
	
	var s = new Simple("Kafka");
	s.y = 4;

	s: s.x[0] --> "Kafka", s.y --> 4
	t: t.x[0] --> "Kafka", t.y --> 3
What happened? First, t was declared to be a Simple object with the default members values x[0] = 9 and y = 3.

Then s is defined to be another Simple object, but changes the defaults to x[0] = "Kafka" and y = 4.

But now t has changed: The x[0] has been changed to "Kafka", but y stays at 3.

The x component is an array. If an element in the array is changed, then it is changed for the prototype and all descendants. The component y is just a number, so changing it in s doesn't affect the other object t, nor the base object. But, changing x[0] in x changes it everywhere.

But if we assign a new array to x in the constructor, then it works the way we expected:

	function Simple(str) {
		this.x = [9];
		if(str!=undefined) {this.x[0] = str;}
	}
	
	Simple.prototype = {x:[],y:3};

	var t = new Simple(): t.x[0] -->9, t.y --> 3
	
	var s = new Simple("Kafka");
	s.y = 4;

	s: s.x[0] --> "Kafka", s.y --> 4
	t: t.x[0] --> 9, t.y --> 3
The first approach didn't change the reference to the array x. The second did. So better be sure to give any arrays new references in the constructor (if that's the goal).