inheritance of wrapped natives via assignment of this.prototype?

32 views
Skip to first unread message

Stephan Beal

unread,
Mar 31, 2009, 9:27:58 AM3/31/09
to v8-users
Hi, all!

Today i figured out that i could extend my class binding framework
with 6 lines of code to allow JS-side classes to extend native bound
classes (i only had to change how the native "this" object is searched
for, recursively checking the prototype object).

i've *almost* got this working, but i'm having a problem understanding
some JS behaviour.

For this example, assume MyNative is a C++ type bound to JS, and it
has a native function, hi(), which is bound to JS. The following
*almost* does what i want, and i'll explain afterwards why it's wrong,
and will ask for help on understanding why my preferred approach isn't
working:


function SubType()
{
return this;
}
SubType.prototype = new MyNative();
var sub = new SubType();
print(sub.hi()); // prints "hi!"

That works, but the prototype of all SubType objects is a single,
shared object, which is obviously going to cause me grief if i'm
subclassing a Window type (all instances would share the same native
window!).

What i'd LIKE to do is:

function SubType()
{
this.prototype = new MyNative();
return this;
}
var sub = new SubType();
print(sub.hi());

But for some reason that's then failing with:

TypeError: Object #<a SubType> has no method 'hi'


Why does the former variant of setting the prototype work, but the
latter does not? How can i achieve the effect i'm looking for? Keep in
mind that the constructor for a given native type might require
certain arguments which only the client can provide, so i cannot
generically provide some default instance of such a type.

:-?

Kasper Lund

unread,
Mar 31, 2009, 9:32:35 AM3/31/09
to v8-u...@googlegroups.com

Super quick and very short reply: You can't change the prototype of an
instance by assigning to the "prototype" property. You need to write
to the (magical) __proto__ property, which is supported - though not
encouraged - by V8.

Cheers,
Kasper

Alex Iskander

unread,
Mar 31, 2009, 9:36:36 AM3/31/09
to v8-u...@googlegroups.com
Setting this.prototype will not have any effect because the prototype
is only used to create new instances of objects. Observe:
function SubType()
{
this.prototype = new MyNative();
//note: although it does no harm, there is actually no need to return
this
}

var sub = new SubType();
sub.hi(); //doesn't exist...

var sub2 = new sub(); //now sub.prototype is used
sub2.hi(); //should exist.


Also, changing the prototype of the instance will probably change the
prototype of all instances.

I'd also like to be able to extend native classes, but I'm not sure
how I'd do it.

Alex
Alex Iskander, TPSi





Stephan Beal

unread,
Mar 31, 2009, 10:04:20 AM3/31/09
to v8-u...@googlegroups.com
On Tue, Mar 31, 2009 at 3:32 PM, Kasper Lund <kas...@chromium.org> wrote:
> Super quick and very short reply: You can't change the prototype of an
> instance by assigning to the "prototype" property. You need to write
> to the (magical) __proto__ property, which is supported - though not
> encouraged - by V8.

My gawd, it works:

function SubType()
{
this.__proto__ = new MyNative();
print('proto =',this.prototype,'str=',this.str);
return this;
}

var sub = new SubType();

sub.str = "sub.str";
var sub2 = new SubType();
sub2.str = "sub2.str";
print(sub.str,sub2.str,sub.me(),sub2.me());
print(sub.hi(),sub2.hi());
print( sub2 instanceof MyNative );

Cool!

i know this is a magic/unportable solution, but is there any real
likelyhood that __proto__ will be removed, or only be available with
certain build- or context-specific options? Or is there another
(portable, though possibly hackish?) approach to doing this?

Thank you, Kasper!

--
----- stephan beal
http://wanderinghorse.net/home/stephan/

Ondrej Zara

unread,
Mar 31, 2009, 10:12:13 AM3/31/09
to v8-u...@googlegroups.com


2009/3/31 Stephan Beal <sgb...@googlemail.com>

__proto__ is the only way to go. To be more specific, this is *exactly* what happens when calling "new Constructor()" - inst.__proto__ is set to Constructor.prototype.

However, not all JS engines permit __proto__ to be set by user (IE for instance forbids this).

O.



 

Stephan Beal

unread,
Mar 31, 2009, 10:22:23 AM3/31/09
to v8-u...@googlegroups.com
On Tue, Mar 31, 2009 at 4:12 PM, Ondrej Zara <ondre...@gmail.com> wrote:
> However, not all JS engines permit __proto__ to be set by user (IE for
> instance forbids this).

i'm only interested in it in the context of subclassing v8-bound
types, so portability across engines isn't a concern :).

:-D

Now i can go open up all kinds of new cans of coding worms...

Louis Santillan

unread,
Mar 31, 2009, 10:29:14 AM3/31/09
to v8-u...@googlegroups.com
I think I found this at KevinLinDev
(http://www.kevlindev.com/tutorials/javascript/inheritance/index.htm).

Object.prototype._extends_ = function( ChildClass, ParentClass )
{
function TempClass() {}
TempClass.prototype = ParentClass.prototype;

ChildClass.prototype = new TempClass();
ChildClass.prototype.constructor = ChildClass;
ChildClass._superConstructor_ = ParentClass;
ChildClass._superClass_ = ParentClass.prototype;
};

And you can use it this way.

var MyType = function()
{
this.prop1 = 1;
};

var MySubType = function()
{
this._superConstructor_.call( this );
this.prop2 = 2;
};

Object._extends_( MySubType, MyType );

var x = new MySubType();

print( x.prop1 + ":" + x.prop2 );

Key is that TempClass that allows you to swap in your parent class.
And I don't see why you couldn't do this for a native-bound type.

-Louis

Stephan Beal

unread,
Mar 31, 2009, 11:37:27 AM3/31/09
to v8-u...@googlegroups.com
On Tue, Mar 31, 2009 at 4:29 PM, Louis Santillan <lpsa...@gmail.com> wrote:
> I think I found this at KevinLinDev
> (http://www.kevlindev.com/tutorials/javascript/inheritance/index.htm).

<big snip>

> Key is that TempClass that allows you to swap in your parent class.
> And I don't see why you couldn't do this for a native-bound type.

Ooooh. i will certainly give that a try. i'm not nearly as intimate
with JS as i am with C++, so i can't come up with this clever stuff
myself.

Thanks :)

Stephan Beal

unread,
Mar 31, 2009, 12:13:27 PM3/31/09
to v8-u...@googlegroups.com
On Tue, Mar 31, 2009 at 4:29 PM, Louis Santillan <lpsa...@gmail.com> wrote:
>   ChildClass._superConstructor_ = ParentClass;
>   ChildClass._superClass_ = ParentClass.prototype;

i wasn't able to get that to work:

subclass.js:24: TypeError: Cannot call method 'call' of undefined
this._superConstructor_.call( this );


but this change works for me:

ChildClass.prototype._superConstructor_ = ParentClass;
ChildClass.prototype._superClass_ = ParentClass.prototype;

note the insertion of ".prototype".

> Object._extends_( MySubType, MyType );

Then i hacked it a bit...

Since MySubType already subclasses Object, we can change:

Object.prototype._extends_ = function( ChildClass, ParentClass )
{

if( 1 == arguments.length )
{
return this._extends_(this,ChildClass);
}
... same as before ...
}

And:

> Object._extends_( MySubType, MyType );

becomes:

MySubType._extends_(MyType);

otherwise _extends_ might as well be a non-member function.

Thanks for that tip! This will be useful!

Louis Santillan

unread,
Mar 31, 2009, 12:56:22 PM3/31/09
to v8-u...@googlegroups.com
On Tue, Mar 31, 2009 at 9:13 AM, Stephan Beal <sgb...@googlemail.com> wrote:
>
> On Tue, Mar 31, 2009 at 4:29 PM, Louis Santillan <lpsa...@gmail.com> wrote:
>>   ChildClass._superConstructor_ = ParentClass;
>>   ChildClass._superClass_ = ParentClass.prototype;

Need to be careful here. The intent is to modify the static class
members here. For the reason why, see below.

>
> i wasn't able to get that to work:
>
> subclass.js:24: TypeError: Cannot call method 'call' of undefined
>  this._superConstructor_.call( this );

...Because I made a typo. For clarity, the _superConstructor_.call()
is referenced via SuperType._superConstructor_.call( this ). This
allows for the selection of which super constructor (really any member
method) you would like to call. So, if you have an inheritance chain
like Person->Employee->Manager, from Manager's constructor you can
call Employee._superConstructor_, or Person._superConstructor_, or
Person.setPhoneNumber or Employee.setPhoneNumber. A nice feature that
call/apply & prototypal inheritance lets you exploit.


>
> but this change works for me:
>
>  ChildClass.prototype._superConstructor_ = ParentClass;
>  ChildClass.prototype._superClass_ = ParentClass.prototype;
>
> note the insertion of ".prototype".
>
>> Object._extends_( MySubType, MyType );
>
> Then i hacked it a bit...
>
> Since MySubType already subclasses Object, we can change:
>
> Object.prototype._extends_ = function( ChildClass, ParentClass )
> {
>    if( 1 == arguments.length )
>    {
>        return this._extends_(this,ChildClass);
>    }
> ... same as before ...
> }
>
> And:
>
>> Object._extends_( MySubType, MyType );
>
> becomes:
>
> MySubType._extends_(MyType);
>
> otherwise _extends_ might as well be a non-member function.
>
> Thanks for that tip! This will be useful!

I don't remember at the moment, but this might breakdown for some
patterns of inheritance. I need re-read this article
(http://webreflection.blogspot.com/2007/07/javascript-prototypal-inheritance-using.html)
to explain why (at one time, I had made the same changes and it didn't
work for me and I forget which test failed).

-Louis

Stephan Beal

unread,
Mar 31, 2009, 1:58:27 PM3/31/09
to v8-u...@googlegroups.com
On Tue, Mar 31, 2009 at 6:56 PM, Louis Santillan <lpsa...@gmail.com> wrote:
> ...Because I made a typo.  For clarity, the _superConstructor_.call()
> is referenced via SuperType._superConstructor_.call( this ).  This

Aha. Okay, for clarity, here's what i've got now (which seems to work):

Object.prototype._extends_ = function( ChildClass, ParentClass )
{
if( 1 == arguments.length )
{

ParentClass = ChildClass;
ChildClass = this;


}
function TempClass() {}
TempClass.prototype = ParentClass.prototype;

ChildClass.prototype = new TempClass();
ChildClass.prototype.constructor = ChildClass;

ChildClass.prototype._superConstructor_ = ParentClass;
ChildClass.prototype._superClass_ = ParentClass.prototype;
}

Object.prototype._extends_ = function( ChildClass, ParentClass )
{

function TempClass() {}
TempClass.prototype = ParentClass.prototype;

ChildClass.prototype = new TempClass();
ChildClass.prototype.constructor = ChildClass;

ChildClass._superConstructor_ = ParentClass;
ChildClass._superClass_ = ParentClass.prototype;

};

var MyType = function()
{
var av = Array.prototype.slice.apply(arguments,[0]);
print("new MyType(",av.join(','),')');
this.prop1 = 1;
};

var MySubType = function()
{
var av = Array.prototype.slice.apply(arguments,[0]);
MySubType._superConstructor_.call( this, av );
print("new MySubType(",av.join(','),')');
this.prop2 = 2;
};


Object._extends_( MySubType, MyType );

var x = new MySubType(7,3,11);

print( x.prop1 + ":" + x.prop2 );

print( x instanceof MyType );


Output:

new MyType( 7,3,11 )
new MySubType( 7,3,11 )
1:2
true


:-?

Stephan Beal

unread,
Apr 1, 2009, 6:14:12 PM4/1/09
to v8-users
On Mar 31, 3:36 pm, Alex Iskander <a...@tpsitulsa.com> wrote:
> I'd also like to be able to extend native classes, but I'm not sure  
> how I'd do it.

For the curious, i've got it working in my class binding framework.
The code to do so is independent of the of the rest of libv8-juice, so
it might serve as a useful starting or studying point for those
wanting to do the same. The overall code is, however, admittedly a bit
involved, and it does some rather confusing things in order to support
inheritance, such that bound member functions will work properly when
called from a subclass of the type of which they are members (i.e. the
_real_ type to which they are actually bound, even though the JS
object might be a subclass).

For those interested in code, go to this page:

http://code.google.com/p/v8-juice/wiki/CreatingJSClasses

grab the 3 source files listed there (see the TOC).

Then go to this page:

http://code.google.com/p/v8-juice/wiki/ClassBinder

Search the TOC for "Inheriting" and go to that section. That shows
what needs to be done.

The actual implementation is "a bit" tied up in the guts of the class
binding framework. It starts here:

http://code.google.com/p/v8-juice/source/browse/trunk/src/include/v8/juice/WeakJSClassCreator.h

in GetSelf(). (Search for "* GetSelf" and you should jump right to
it). The JS-side inheritance is supported by that function's calls to
GetSelfFromPrototype(), which is basically a recursive call back to
GetSelf(), and on each call it passes this.prototype, recursing up the
prototype chain until no objects are found.

As a proof of concept i've sucessfully subclassed curses window
objects from JS code, as demonstrated on this page:

http://code.google.com/p/v8-juice/wiki/PluginNCurses

(Search for "Inheriting" and you'll find the TOC entry/link.)

Happy hacking!
Reply all
Reply to author
Forward
0 new messages