constructor for a derived object is same as that of its parent


S

satyajit

I am trying to learn the concept of constructors in ECMAScript. I
executed following code (See execution in Rhino JavaScript shell):
function Foo(a)
{
this.a = a;
}

function Bar(b)
{
this.b = b;
}
Bar.prototype = new Foo(1);
var x = new Foo(2);
var y = new Bar(3);

Now I expect y.constructor to give me Bar, but I am getting Foo.
Can anybody explain?

Following the execution sequence in Rhino JavaScript shell:

Rhino 1.6 release 5 2006 11 18
js> function Foo(a)
{
this.a = a;
}
js> function Bar(b)
{
this.b = b;
}
js> Bar.prototype = new Foo(1);
[object Object]
js> var x = new Foo(2);
js> var y = new Bar(3);
js> x.constructor

function Foo(a) {
this.a = a;
}

js> y.constructor

function Foo(a) {
this.a = a;
}

Regards,
Satyajit
 
Ad

Advertisements

V

VK

satyajit said:
function Foo(a)
{
this.a = a;
}

function Bar(b)
{
this.b = b;
}
Bar.prototype = new Foo(1);
var x = new Foo(2);
var y = new Bar(3);

Now I expect y.constructor to give me Bar, but I am getting Foo.

Yep... And at the same
alert(y instanceof Bar); // true
or (the same but more "conceptual"):
alert(Bar.prototype.isPrototypeOf(y)); // true

Tricky, is not it? ;-)

I highly suggest you to read and study all samples from:

Eric Lippert
"The JScript Type System, Part Two: Prototypes and constructors"
<http://blogs.msdn.com/ericlippert/archive/2003/11/06/53352.aspx>

This rather short blog post is the *only one* source about JavaScript
inheritance currently existing in the Internet. There can be more of
course, but my two years search did not reveal them. Anything else I
could find is either an erroneus crap or multi-paged revelations
leaving you even more confusing then ever before.

P.S. Eric Lippert is the original maker of Microsoft JScript engine.
That doesn't mean that you have to take each his word as the final
truth: there is a difference between a piano maker and a piano player
:) I just thought that it should be mentioned that he is not some
"side ECMAScript specialist".
 
M

Martin Honnen

satyajit said:
I am trying to learn the concept of constructors in ECMAScript. I
executed following code (See execution in Rhino JavaScript shell):
function Foo(a)
{
this.a = a;
}

function Bar(b)
{
this.b = b;
}
Bar.prototype = new Foo(1);
var x = new Foo(2);
var y = new Bar(3);

Now I expect y.constructor to give me Bar, but I am getting Foo.
Can anybody explain?

This has confused a lot of people but it only shows that the constructor
property is not very intuitive respectively not set in a way that
follows the intuition people have that think in class based terms and
expect "instances" in JavaScript to have a property named constructor
that points to the function constructor.

You might want to check
y.hasOwnProperty('constructor')
and you will find that y does not have an own property of that name.
That means when y.constructor is looked up that the prototype chain is
used to look for the property and so the lookup first looks at
Bar.prototype having a constructor property but it does not have one meaning
Bar.prototype.hasOwnProperty('constructor')
is false too, then follows the lookup of Foo.prototype and
Foo.prototype.hasOwnProperty('constructor')
is true and that constructor property has been set when the Foo function
was created according to section 13.2 of the ECMAScript edition 3
specification.
 
R

Richard Cornford

satyajit said:
I am trying to learn the concept of constructors in ECMAScript. I
executed following code (See execution in Rhino JavaScript shell):
function Foo(a)
{
this.a = a;
}

function Bar(b)
{
this.b = b;
}
Bar.prototype = new Foo(1);
var x = new Foo(2);
var y = new Bar(3);

Now I expect y.constructor to give me Bar, but I am getting Foo.
Can anybody explain?
<snip>

When a function object is created it is given a - prototype - property
and that property is assigned a value that is a reference to an object.
That object is then given a - constructor - property that is assigned a
reference to the function object.

If the function object is used to construct an object the value
currently assigned to its - prototype - property is assigned to the new
object's internal [[Prototype]] property, which is then used as the
first object in the new object's prototype chain. Thus this new object
inherits a reference to the function object used as its constructor
through its prototype chain.

You have replaced the object originally assigned to - Bar.prototype -
with an instance of Foo. Thus the object that had the - constructor -
property that referred to Bar has been replaced with an object that is
inheriting a constructor - property through its prototype chain from -
Foo.prototype -. And when that object is assigned to the internal
[[Prootype]] properties of objects created with - new Bar - they
inherit its - constructor - property.

You can mitigate that issue by doing:-

Bar.prototype = new Foo(1)
Bar.prototype.constructor = Foo;

- and then new instances of Bar will inherit a - constructor - property
that refers to - Foo -.

However, you really should not be writing javascript code in which you
need to test for the constructor of any objects (outside of
experimental and testing code). There are no casting issues in
javascript as all objects are really of a single 'class' and in a
loosely typed language like javascript it is a necessary discipline for
the programmer to be keeping track of the types being used and so
situations where you need to query a type at runtime are exceptional.

Richard.
 
M

Martin Honnen

Richard said:
You can mitigate that issue by doing:-

Bar.prototype = new Foo(1)
Bar.prototype.constructor = Foo;

- and then new instances of Bar will inherit a - constructor - property
that refers to - Foo -.

How does that mitigate things? The original poster complained about Foo
being returned by y.constructor and wanted Bar instead. That code above
does not change that, unless you wanted to set
Bar.prototype.constructor = Bar;
 
R

Richard Cornford

Martin said:
How does that mitigate things? The original poster complained about Foo
being returned by y.constructor and wanted Bar instead. That code above
does not change that, unless you wanted to set
Bar.prototype.constructor = Bar;

You are right. I intended to type Bar and typed Foo instead.

Richard.
 
Ad

Advertisements

V

VK

Peter said:

Video is good (though the downstream from the Yahoo server seems poor
as it chocks even on my DSL).

The inheritance article (the first one) is a rather dangerous reading.
It is one of countless "C++ over JavaScript" tutorials: a little
preface about prototype inheritance (just few words do not make a
C++'er too much bored) and then quickly move onto the main part: how to
build inheritance in the "conventional proper" way and do not be
bothered with that stupid prototypes anymore. Such articles constitute
90% of "inheritance in JavaScript" sources, but this one admittedly is
of much better quality than many of what I've seen.

I mean hell - I use "classy" emulation in JavaScript left and right
myself because it's often more easy and quickly in a mixed environment.
But if anyone is targeted to understand how is it *really* ticking
(even if never come to it again and just stick to say prototype.js)
then such articles should be read only after the one I posted.
 
J

John G Harris

However, you really should not be writing javascript code in which you
need to test for the constructor of any objects (outside of
experimental and testing code). There are no casting issues in
javascript as all objects are really of a single 'class' and in a
loosely typed language like javascript it is a necessary discipline for
the programmer to be keeping track of the types being used and so
situations where you need to query a type at runtime are exceptional.

To add to that, the 'constructor' property is like 'with' and two-digit
year numbers : each seemed a good idea at the time, but in practice each
causes a lot of confusion and can be done better a different way.

John
 
S

satyajit

Martin said:
How does that mitigate things? The original poster complained about Foo
being returned by y.constructor and wanted Bar instead. That code above
does not change that, unless you wanted to set
Bar.prototype.constructor = Bar;

Thanks VK, Martin, and Richard for very explanations. Got the reason
after I read Eric Lippert blog.

I did not want to set "Bar.prototype.constructor = Bar;" but was trying
to find out how the constructor and prototypes work. The version of
ECMAScript standard that I got originally from ECMA's site was ECMA-262
standard and that did not have reference to instanceof and
isPrototypeOf(). Getting these concepts from the standard was very
difficult.

However one thing I noticed in the ECMA standard that the String
behaves differently when called as constructor and as function. I
wanted to explore how to do that for my own functions that can be used
as constructor or as conversion type. I tried to make sense of some
earlier posts but could not do so.

- Satyajit
 
V

VK

satyajit said:
The version of
ECMAScript standard that I got originally from ECMA's site was ECMA-262
standard and that did not have reference to instanceof and
isPrototypeOf(). Getting these concepts from the standard was very
difficult.

The current standard is ECMA-262 3rd edition, so you must be reading
ECMA-262 2nd edition (same standard but the obsolete edition). The 3rd
(current) edition as available at
<http://www.ecma-international.org/publications/standards/Ecma-262.htm>

Please note though that - despite of what the name implies - this
standard describes an ECMAScript-compliant *script engine* so primary
targeted to UA producers and not to the end developers. This way
learning the JavaScript programming by ECMA-262 specs is like learning
to drive by car's technical specs: theoretically possible, practically
very difficult :)
From the online resources linked at
<http://www.jibbering.com/faq/#FAQ3_2> I would suggest first MSDN
JScript 5.6 documentation as the most consistent (though still not
bug-free):
<http://msdn.microsoft.com/library/d...html/29f83a2c-48c5-49e2-9ae0-7371d2cda2ff.asp>

It is available for download (.chm help file, 2.8Mb) at
<http://www.microsoft.com/downloads/details.aspx?familyid=01592C48-207D-4BE1-8A76-1C4099D7BBB9>
A "Genuine Windows validation" is required, also chm help files need IE
installed to be viewed - so alas this download is useless for many
developers.

(JScript 5.6 is the one running on IE 6 and - with one minor
modification - on IE 7. It corresponds to Mozilla JavaScript 1.5 minus
all Mozilla/Microsoft extensions).

However one thing I noticed in the ECMA standard that the String
behaves differently when called as constructor and as function.

I'm not sure if I fully understand this statement. There can be string
primitive value and string object in JavaScript: the first one created
implicitly out of string literal, the second one created explicetly by
using String constructor: new String(value) The explicit String
constructor is very rarely used in JavaScript.
I wanted to explore how to do that for my own functions that can be used
as constructor or as conversion type. I tried to make sense of some
earlier posts but could not do so.

In JavaScript the call context defines everything: so the very same
function can act as an object constructor or as a regular subroutine,
depending on how did you call it. Some formal description of your
actual task would be helpful.
 
Ad

Advertisements

S

satyajit

VK wrote:
In JavaScript the call context defines everything: so the very same
function can act as an object constructor or as a regular subroutine,
depending on how did you call it. Some formal description of your
actual task would be helpful.

Thanks for the resources. To give an example (not a real example) of
what I was trying to find out is as follows:

function MimeType(string extension)
{
if ( /* its a constructor - this is what I want to know how to
check. */ )
{
// initialize MimeType. For jpeg, description is 'image/jpeg'
// Assume that getDescriptionFromExtension() returns a string
// with appropriate description
this.description = getDescriptionFromExtension(extension);
// other initializers..
}
else return getDescriptionFromExtension(extension);
}

I could use it like
var img1 = new MimeType(".jpeg");
img1.description would then be "image/jpeg"

another probable usage:

if ("image/jpeg" == MimeType(extension));

Any quick ideas?

Thanks.
-Satyajit
 
Ad

Advertisements

V

VK

satyajit said:
To give an example (not a real example) of
what I was trying to find out is as follows:

function MimeType(string extension)
{
if ( /* its a constructor - this is what I want to know how to
check. */ )
{
// initialize MimeType. For jpeg, description is 'image/jpeg'
// Assume that getDescriptionFromExtension() returns a string
// with appropriate description
this.description = getDescriptionFromExtension(extension);
// other initializers..
}
else return getDescriptionFromExtension(extension);
}

I could use it like
var img1 = new MimeType(".jpeg");
img1.description would then be "image/jpeg"

another probable usage:

if ("image/jpeg" == MimeType(extension));

Any quick ideas?

The first one is that I'm not excited by this approach ;-) IMO using
the same entity as object constructor and as subroutine depending on
circumstances is a bad practice: even if the used language technically
allows it. It obfuscates the source and it promises many problems with
inheritance (if anyone ever extends your library). You may flush my
opinion out though.

If function is called as constructor, [this] points to the newly
created object instance. (That's if no one is trying to cheat on your
program by substituting context: say by using MimeType.call(null,
args);
Otherwise [this] will point to the Global context which is equal to the
current window object in comparison operations (that's again for a
plain-vanilla situations without any special cases).
So if you insist on the current approach, you may check this == self :

function MimeType(ext) {
if (this == self) {
// presumably called as simple function
return 'foobar';
}
else {
// presumably called as constructor
this.description = 'foobar';
}
}

var foo = new MimeType();
var bar = MimeType();

alert(foo.description); // 'foobar'
alert(bar); // 'foobar'


P.S. I would still disassemble constructor from subroutine. Just try -
you may like it :)
 

Top