I'm trying to implement some kind of class hierarchy in JavaScript. I think I understood the prototype chain, but I still have to sort out the constructor-chaining. Following David Flanagan's Definitive Guide, I wrote
function DerivedClass()
{
BaseClass.apply(this, arguments); // chain constructors
// do some initializations specific to DerivedClass...
}
var foo = new DerivedClass();
where BaseClass()
is a native function of mine written in C++ (I'm
using QtScript). My problem is that then, BaseClass()
is called
as a function, not as a constructor.
I could code BaseClass()
to always behave as a constructor, however it
is called. But I am afraid some day one of my users might forget new
and just write
var bar = BaseClass();
In such a situation, I would like BaseClass()
to do something more
sensible than initializing the global object. For example:
if (!context->isCalledAsConstructor()) fail_gracefully();
But then the constructor chaining fails!
Is there a way I can chain the constru开发者_开发百科ctors and have BaseClass()
actually be called as a constructor? Or should I just educate my users
to never forget new
? Right now I'm tempted to replace the test above
by:
if (context->thisObject().strictlyEquals(engine->globalObject()))
fail_gracefully();
but I wonder if there is a cleaner way to handle this.
Thanks!
You should educate your users to never forget new
.
All "constructors" in JavaScript are just functions after all, so you can't protect against a constructor being called as a function.
It's incorrect JavaScript to try and create a new object without using new
. Just because there isn't a "compile time warning" as there is in Java, doesn't make it any different.
Answering myself...
I thought about my problem overnight... and I think I found a somewhat
more satisfying solution: behave as a constructor if this
is an
instance of the calle. This test is somewhat stricter than checking
whether it's not the global object, but it still allows constructor
chaining, as long as the prototypes have been properly chained.
Here are the first lines of my native constructor (SerialPort
is my
base class, built around QSerialDevice):
/*
* Should we behave as a constructor?
*
* We could use context->isCalledAsConstructor() to decide. However,
* we may want to subclass SerialPort in JavaScript and chain the
* constructors:
*
* function DerivedClass()
* {
* SerialPort.apply(this, arguments);
* // do some more initializations...
* }
*
* This would fail if we decided on the basis of
* context->isCalledAsConstructor(). The test below is somewhat less
* strict. It allows constructor chaining provided the prototypes
* have been properly chained.
*/
bool behave_as_constructor =
context->thisObject().instanceOf(context->callee());
The funny thing about this is: unlike isCalledAsConstructor()
, this
test can also be implemented in a JavaScript constuctor!
精彩评论