Extending an Object Whose Constructor Takes Arguments

I’m sure this question has been asked many times before, but I can’t seem to get the correct terms to find the answer in Google.

I understand basic JavaScript inheritance. However, everything I’ve read makes the inheritance work something like this:

var ChildClass = new Function();
ChildClass.prototype = new ParentClass();
ChildClass.constructor = ChildClass;

That’s fine, but what happens when ParentClass’s constructor needs parameters?

As an example, look at the following code:

function Furniture(color){
	this.color = color;
	this.sayColor = function(){
		alert(this.color);
	}
}

var Chair = new Function();
Chair.prototype = new Furniture();
Chair.constructor = Chair;

var a = new Furniture("red");
a.sayColor(); // alerts "red"

var b = new Chair("green");
b.sayColor(); // alerts "undefined"

How could I “copy” Furniture’s constructor into the Chair object? Better yet, how could I copy Furniture’s constructor and then add some functionality to it?

The prototype is the bog-standard “always the same” template that each object is created from.

So knowing that, you would need to supply the constructor with the ability to perform the change, or at least, to request that the change be performed.


function Furniture(color) {
    this.setColor = function (color) {
        this.color = color;
    };
    this.sayColor = function () {
        alert(this.color);
    };
    this.setColor(color);
}
var Chair = function (color) {
    this.setColor(color);
};
Chair.prototype = new Furniture();
Chair.constructor = Chair;

Hi pmw57,

Thanks for the reply! That makes sense. But what if you’re extending an object for which you don’t know what the “constructor” does? For example, say I wanted to make a “SalesTime” object that extends the Date object but adds new properties and methods. Date’s constructor takes optional arguments, though.

I came up with the following code:

function Furniture(color) {
	this.setColor = function (color) {
		this.color = color;
	}
	this.sayColor = function () {
		alert(this.color);
	}
	this.setColor(color);
}

var Couch = function(){
	this.parent.constructor.apply(this, arguments);
}
Couch.constructor = Couch;
Couch.prototype.parent = Furniture.prototype;

var SalesTime = function(){
	this.parent.constructor.apply(this, arguments);
}
SalesTime.constructor = SalesTime;
SalesTime.prototype.parent = Date.prototype;

var c = new Couch("blue");
c.sayColor(); // alerts "blue"

var d = new SalesTime("Sat, 01 Jan 2011 08:07:53 GMT");
alert(d.getTime()); // FireBug reports "d.getTime() is not a function"

As you can see, I got the Couch class to work without re-writing any kind of constructor. However, the same thing doesn’t work for the built-in Date class.

What’s the next step?

Here is the original javascript modified to include a Chair object method that creates a new Furniture object. This technique allows the chair object to call the Furniture object sayColor() method. You will note that I modified the sayColor() method to allow all of the Chair object properties to be displayed.


<script type="text/javascript">
<!--
// Furniture object constructor
function Furniture(color)
    { this.color = color;
      // saycolor method uses chair properties here
      this.sayColor = function(){ alert("Style= "+this.style+" age= "+this.age+" color= "+this.color); }
     }
 //
// Chair object constructor
 function Chair(chairStyle,age,color)
  { this.style=chairStyle;
    this.age=age;
   // using the item method here creates a new Furniture obj
    this.item=Furniture;
    this.item(color);
  }
//
 /*
// using this will return undefined for attributes other than color
var a = new Furniture("red");
a.sayColor(); // alerts "red"
 */
//
//  create a new Chair object
var b = new Chair("bentwood","modern","green");
//
// the Chair object now has access to the Furniture object method sayColor()
// returns all of the properties of the chair object using the Furniture object method

b.sayColor();
//
//-->
</script>


What do you make of this resource?
Correct OOP for JavaScript

AllanP: Thanks for the comment, but your code still doesn’t solve the problem of passing arguments to the parent’s constructor.

pmw57: That is a very helpful resource! It answers most of my questions.

I now have the following code, which uses modified versions of the “Inherits” methods recommended in the article:

Object.prototype.Inherits = function(parent, args){
	if(args){
		parent.apply(this, args);
	}else{
		parent.call(this);
	}
}
Function.prototype.Inherits = function(parent){
	this.prototype = new parent();
	this.prototype.constructor = this;
}

function Furniture(color) {
	this.setColor = function (color) {
		if(color) this.color = color;
	}
	this.sayColor = function () {
		alert(this.color);
	}
	this.setColor(color);
}

Couch.Inherits(Furniture);
function Couch(){
	this.Inherits(Furniture, arguments);
}

SalesTime.Inherits(Date);
function SalesTime(){
	this.Inherits(Date, arguments);
}

var c = new Couch("blue");
c.sayColor(); // alerts "blue"

var d = new SalesTime("Sat, 01 Jan 2011 08:07:53 GMT");
alert(d.getTime()); /* Throws an error
FireBug says: "Date.prototype.getTime called on incompatible Object"
WebKit Inspector says: "TypeError: this is not a Date object."
Opera Dragonfly says: "TypeError: Date.prototype.getTime: this is not a Date object"
Internet Explorer 8 Inspector says: "'[object]' is not a date object"
Even iCab 3 says: "This method can only be used on a Date object"
*/

It works perfectly for my custom Chair inheriting from the Furniture object. However, as you can see, it fails on my SalesTime inheriting from the built-in Date object.

Why does this method of inheritance work for custom objects but not for built-in objects? What method could be used to allow for inheriting from built-in objects?

So we’re getting a type error, because there are differences when inheriting from class objects and when inheriting from base objects.

Here are some bigger brains than mine, discussing the problem.
[http://www.mail-archive.com/v8-users@googlegroups.com/msg02161.html](Prototypal Inheritance from Native base - Bug?)

There may be a possible solution at:
http://www.mail-archive.com/v8-users@googlegroups.com/msg02169.html

Or, you could script the date support, or parts of it that you need, from:
http://code.google.com/p/v8/source/browse/trunk/src/date.js?spec=svn6131&r=6131

Hmm. I never knew the V8 behind-the-scenes source code was open. It’s interesting seeing how it all works.

I noticed these functions defined in the JavaScript file:

function ThrowDateTypeError() {
  throw new $TypeError('this is not a Date object.');
}
// ...
function DateSetTime(ms) {
  if (!IS_DATE(this)) ThrowDateTypeError();
  return %_SetValueOf(this, TimeClip(ToNumber(ms)));
}

That seems relevant to the inheritance of the native Date object. So, I searched the V8 “truck” to find calls to that function. This code, which is in macros.py, calls that function in the following excerpt:

macro IS_DATE(arg)              = (%_ClassOf(arg) === 'Date');
# ...
macro DATE_VALUE(arg) = (%_ClassOf(arg) === 'Date' ? %_ValueOf(arg) : ThrowDateTypeError());

Meanwhile, back in date.js, the DATE_VALUE and/or the IS_DATE functions (macros?) are called in the majority of Date method calls.

So, I guess what needs to be done is to make the child object still be of the “class” Date. I couldn’t find anything in the V8 source that defined “%_ClassOf”, and I’m not familiar with Python, so I don’t know exactly what V8 is looking for when it tests a class name.

The discussions you linked to are complicated, but I might try re-reading them again tomorrow and see if I can pull something out of them. And I guess re-scripting might work, but it just seems redundant when you can make use of the optimized and often-updated native Date class. I might just use a wrapper.

Hmm, I just tried the following code:


function SalesTime(){
	var argsArr = [];
	for(var i=0; i<arguments.length; i++){
		argsArr.push('arguments['+i+']');
	};
	eval('var base = new Date('+argsArr.join(",")+');');
	base.__proto__ = SalesTime.prototype;
	return base;
}
SalesTime.prototype = {
	__proto__: Date.prototype,
	constructor: SalesTime,
	getTimeAddOne: function(){
		return this.getTime()+1;
	}
};

var a = new SalesTime("Sat, 01 Jan 2011 08:07:53 GMT");
document.write(a.getTime()+" "+a.getTimeAddOne());

And… it runs as expected with no Type errors!

I got the starting point for this code from the second link you provided. To be completely honest, I’m not really sure exactly what all the "proto"s mean, but I’m glad that it works.

It’s a non-standard method, much like innerHTML. There’s no guarantee that web browsers will support it, but if it works in the environments you care about then that’s what matters.

JavaScript-DOM Prototypes in Mozilla

[indent]Non Standard
No browser is required to provide modifiable proto, nor a global Node, nor provide any way to get at host objects nor their associated prototypes. If such objects are provided, they are not guaranteed by any specification to have any effect on the environment. Results in other browsers is not guaranteed.[/indent]