Cancelling instantiation

M

Michael Winter

Other than throwing an exception during the execution of an object's
constructor, is there any way to cancel creation of the object in such a
way that it leaves a reference to it null or undefined. That would enable
the simple test:

var ref = new someObject();

if( ref ) {
// Object created successfully
}

I know one could use state flags instead, but the object I have in mind
shouldn't exist if the arguments passed to it are incorrect (there is a
good reason, trust me).

[OT - Exceptions in general (including other languages)]

Some people seem to have issues concerning throwing an exception during
object construction to signify failure and instead prefer to use state
flags. Any thoughts either way?

[/OT]

By the way, it's not possible to have protected[1] members in JavaScript,
is it? Shame...

Many thanks,
Mike


[1] I do mean protected, not private.
 
R

Richard Cornford

Michael said:
Other than throwing an exception during the execution of an object's
constructor, is there any way to cancel creation of the object in
such a way that it leaves a reference to it null or undefined.

No, the only circumstances where a call to the internal [[Construct]]
method of a constructor will not result in the return of the created -
this - object is when the internal call to the [[Call]] method returns a
value of Type object (as opposed to typeof "object").
That would enable the simple test:

Maybe you could return an object that can be (probably indirectly)
converted to boolean false in a way that could not normally be achieved
with the constructed object. EG:-

function Const(arg1){
if(!arg1){
return (new String(""));
}
}

var a = new Const(false);

if(String(a)){
alert('Object constructed');
}else{
alert('Empty string object returned');
}

- but it would be important that the object not overload the
Object.prototype.toString method in such a way as to (ever) return an
empty string. Or:-

function Const(arg1){
if(!arg1){
return (new Number(0));
}
}

var a = new Const(false);

if(Number(a)){
alert('Object constructed');
}else{
alert('Empty string object returned');
}

[OT - Exceptions in general (including other languages)]

Some people seem to have issues concerning throwing an exception
during object construction to signify failure and instead prefer
to use state flags. Any thoughts either way?

For cross-browser client-side scripting try-catch cannot yet be used
because the older of the browsers currently in use regard them as
reserved words, generate syntax errors as the code loads and never even
attempts to execute it. (Which is one of the reasons that so much effort
gets put into testing the environment, so exceptions are not thrown and
there is no need to handle them.)
By the way, it's not possible to have protected[1] members in
JavaScript, is it? Shame...
[1] I do mean protected, not private.

They can be emulated but the effort, introduced overheads and code bloat
would probably negate any advantage.

Richard.
 
R

Richard Cornford

Richard Cornford wrote: said:
... . Or:-

function Const(arg1){
if(!arg1){
return (new Number(0));
}
}

var a = new Const(false);

if(Number(a)){
alert('Object constructed');
}else{
alert('Empty string object returned');
}
<snip>

That version will not work as the object would be converted to NaN,
which would than convert to boolean false. Unless maybe the object
deliberately overloaded the valueOf method, or the returned Number
object was non zero and the logic of the test reversed. Probably better
just to use the String version.

Richard.
 
M

Michael Winter

Michael said:
Other than throwing an exception during the execution of an object's
constructor, is there any way to cancel creation of the object in
such a way that it leaves a reference to it null or undefined.

No, the only circumstances where a call to the internal [[Construct]]
method of a constructor will not result in the return of the created -
this - object is when the internal call to the [[Call]] method returns a
value of Type object (as opposed to typeof "object").

I can work with that...
Maybe you could return an object that can be (probably indirectly)
converted to boolean false in a way that could not normally be achieved
with the constructed object. EG:-

function Const(arg1){
if(!arg1){
return (new String(""));
}
}

var a = new Const(false);

if(String(a)){
alert('Object constructed');
}else{
alert('Empty string object returned');
}

- but it would be important that the object not overload the
Object.prototype.toString method in such a way as to (ever) return an
empty string.

[snipped number-based, problematic approach]

After reading the above, and the latest JavaScript reference (by Netscape)
concerning exceptions, I took a slightly different approach:

function Exception( msg ) {
this.message = msg;
}

function Obj( err ) {
if( err ) return new Exception( 'Some text' );

// Continue with object construction...
}

var a = new Obj( true );

if( a instanceof Exception ) {
// Error during construction...

Once exceptions in JavaScript have matured, this could be changed to:

function Obj( err ) {
if( err ) throw new Exception( 'Some text' );

// Continue with object construction...
}

try {
var a = new Obj( true );
} catch( e ) {
// Error during construction...
}

It appears to be an acceptable solution, and would provide a trivial
conversion to exceptions further down the road.
For cross-browser client-side scripting try-catch cannot yet be used
because the older of the browsers currently in use regard them as
reserved words, generate syntax errors as the code loads and never even
attempts to execute it. (Which is one of the reasons that so much effort
gets put into testing the environment, so exceptions are not thrown and
there is no need to handle them.)

You don't happen to know when exceptions were introduced, do you? I was
quite surprised to find that IE 5 is supposed to support exceptions. I'm
sure someone doubted that even IE 6 supported them.
By the way, it's not possible to have protected[1] members in
JavaScript, is it? Shame...
[1] I do mean protected, not private.

They can be emulated but the effort, introduced overheads and code bloat
would probably negate any advantage.

That's what I thought. Just checking that I wasn't missing anything.

Thanks Richard,
Mike
 
M

Michael Winter

On Tue, 02 Mar 2004 13:55:07 GMT, Michael Winter

[snip]
try {
var a = new Obj( true );
} catch( e ) {
// Error during construction...
}

That should be more like:

var a = null;

try {
a = new Obj( true );
} catch( e ) {
// Error during construction...
}

Mike
 
R

Richard Cornford

Michael said:
Richard Cornford wrote:
After reading the above, and the latest JavaScript reference (by
Netscape) concerning exceptions, I took a slightly different approach:

function Exception( msg ) {

I would be concerned whether "Exception" was already defined in any
execution environment. It doesn't seem that unlikely a name to be in
use, maybe "ObjectConstructionException" or something shorter.

if( a instanceof Exception ) {

I think - instanceof - may have entered the language at about the same
point as try-catch. (I have got a copy of the 3nd edition of ECMA 262
which I could e-mail you if you are interested)

You don't happen to know when exceptions were introduced, do you? I
was quite surprised to find that IE 5 is supposed to support
exceptions. I'm sure someone doubted that even IE 6 supported them.

ECMAScript 3rd edition, JavaScript 1.4 and JScript 5.

That's what I thought. Just checking that I wasn't missing anything.

For completeness here is how it could be done:-

The Emulation of Protected Members with Javascript.

The concept of package does not exist in javascript so it is not
practical to define protected in terms of accessibility only to members
of the same package and subclass as in Java. Instead protected is going
to be considered in terms of restricting the accessibility of members to
objects that belong to the same "Class" (built with the same
constructor) or objects that belong to a group of pre-defined related
"Classes". The potential for the use of protected members in subclasses
exists, to the extent that this type of javascript object can be
subclassed.

While it is possible for private instance members (including method) to
be successfully emulated with javascript, for interaction between object
the getters and setters for any protected functionality will themselves
have to be public. Data members will be implemented as private members
with getters and setters to control their accessibility to other
classes. The emulation cannot be done in terms of the visibility of the
getters and setters, instead their willingness to act will be restricted
to interactions initiated by a limited set of classes.

To restrict interactions to a within limited set of classes those
classes are going to need a way of identifying each other as belonging
to that set. This can be done with a shared secret, but it is going to
have to be a secret that cannot be accessed outside of the set of
classes. Similarly there will need to be a mechanism to pass that secret
between classes in the set without exposing the secret and in a way that
cannot be hijacked by external code.

In the same way as closures provide a method of emulating private static
and instance members that are not accessible outside of the Class or
instance in question, they can be used to hold information that is only
available to a group of classes:

(function(){
/* Assign a reference to the global object (this in a function
expression executed inline) to the local variable - global
- so that properties of the global object can be created
relative to it:
*/
var global = this;
/* Create an object to act as the shared secret for the two (or
possibly more) constructors defined during the execution of
this inline function expression. Later, when the complexity
of this closer increases to include (private) method at this
level, one of those methods may be used instead of this
object:
*/
var secret = {};
/* Execute another function expression inline such that it returns
a function object that can act as the constructor for objects
and assign that constructor to a global property called
"ClassA":
*/
global.ClassA = (function(){
/* This inner function definition will act as the constructor
for "ClassA":
*/
function constr(){
/* We do not want our constructor being called as a
function:
*/
if(this == global)return;
/* Ensure that each object instance created with this
constructor holds a reference to its object instance
(will be employed later to ensure that protected
methods can only be executed as methods of this object):
*/
var self = this;
}
/* return a reference to the inner function defined above:
*/
return constr;
})();
/* Execute another function expression inline such that it returns
a function object that can act as the constructor for objects
and assign that constructor to a global property called
"ClassB":
*/
global.ClassB = (function(){
/* This inner function definition will act as the constructor
for "ClassA":
*/
function constr(){
/* We do not want our constructor being called as a
function:
*/
if(this == global)return;
/* Ensure that each object instance created with this
constructor holds a reference to its object instance
(will be employed later to ensure that protected
methods can only be executed as methods of this object):
*/
var self = this;
}
/* return a reference to the inner function defined above:
*/
return constr;
})();
})();

That is the basic framework. It doesn't have any emulation of protected
members yet but the variable - secret - is only available from code
defined within the outer function expression. That code includes that
two class constructors so all instances created with those constructors
has access to the - secret - variable and can use the unique identity of
that object to verify that instructions to access any members it is
regarding as protected are only modified by code that shares that
secret. Code external to the outer function expression cannot access
the - secret - object.

Now to add the mechanics for getting and setting the protected members
and executing (publicly exposed but protected in execution) methods. My
thirst thought in this direction had the - secret - object passed by
reference with the various method calls but it occurred to me that that
would allow the objects to be tricked into giving up their secret. The
secret must stay within its closure, as must any protected data, so
instead I am going to use a series of variables within the same level of
the closure as - secret - to provide a transfer location for the values
and a semaphore mechanism for the requests:

(function(){
var global = this;
var secret = {};
var requestToRead = null;
var requestToSet = null;
var requestToExecute = null;
var validTransfer = null;
var transferValue;
global.ClassA = (function(){
function constr(){
if(this == global)return;
var self = this;
var protectedMemberOfA = 5;
this.getProtectedMemberOfA = function(){
/* First ensure that the function is being executed as
a method of this object:
*/
if(this == self){
/* Check to see that the - requestToGet - variable
has been set to the correct secret:
*/
if(requestToGet == secret){
/* All is well so expose the -
protectedMemberOfA - value to the calling
object (indirectly via the private -
transferValue - variable):
*/
transferValue = protectedMemberOfA;
validTransfer = secret;
}
}
/* Null the - requestToGet - variable:
*/
requestToGet = null;
}
this.setProtectedMemberOfA = function(){
/* First ensure that the function is being executed as
a method of this object:
*/
if(this == self){
/* Check to see that the - requestToSet - variable
has been set to the correct secret:
*/
if(requestToSet == secret){
/* All is well so set the - protectedMemberOfA
- (indirectly from the private -
transferValue - variable):
*/
protectedMemberOfA = transferValue;
}
}
/* Null the - requestToSet - variable:
*/
requestToSet = null;
}
this.incrementProtectedMemberOfA = function(){
/* First ensure that the function is being executed as
a method of this object:
*/
if(this == self){
/* Check to see that the - requestToExecute -
variable has been set to the correct secret:
*/
if(requestToExecute == secret){
/* All is well, so act: */
protectedMemberOfA++;
}
}
/* Null the - requestToExecute - variable:
*/
requestToExecute = null;
}
this.passOnProtMemOfAToClassBInst = function(classBinst){
/* First ensure that the function is being executed as
a method of this object and that the object passed
by reference has the required method:
*/
if((this == self)&&(classBinst.setProtectedMemberOfB)){
/* Set up the interaction with the instance of
ClassB by assigning - secret - to the -
requestToSet - variable:
*/
requestToSet = secret;
/* The place the value of - protectedMemberOfA - in
a location that is accessible to instances of
the appropriate classes:
*/
transferValue = protectedMemberOfA;
/* Call the protected setter on the instance of
ClassB:
*/
classBinst.setProtectedMemberOfB();
/* Null the - requestToSet - variable:
*/
requestToSet = null;
}
}
this.setProtMemOfAToClassBInstProM = function(classBinst){
/* First ensure that the function is being executed as
a method of this object and that the object passed
by reference has the required method:
*/
if((this == self)&&(classBinst.getProtectedMemberOfB)){
/* Set up the interaction with the instance of
ClassB by assigning - secret - to the -
requestToGet - variable:
*/
requestToGet = secret;
/* Call the protected getter on the instance of
ClassB:
*/
classBinst.getProtectedMemberOfB();
/* If the call was to a valid getter the -
requestToGet - variable should have been nulled,
and successful data transfer will be marked by
the - validTransfer - variable knowing the
secret:
*/
if((!requestToGet)&&(validTransfer == secret;)){
/* Assign the value of the - transferValue -
variable to the local protected member:
*/
protectedMemberOfA = transferValue;
}
/* Null the - requestToGet - variable: */
requestToGet = null;
/* Null the - validTransfer - variable: */
validTransfer = null;
}
}
}
return constr;
})();
global.ClassB = (function(){
function constr(){
if(this == global)return;
var self = this;
var protectedMemberOfB = 8;
this.getProtectedMemberOfB = function(){
/* First ensure that the function is being executed as
a method of this object:
*/
if(this == self){
/* Check to see that the - requestToGet - variable
has been set to the correct secret:
*/
if(requestToGet == secret){
/* All is well so expose the -
protectedMemberOfB - value to the calling
object (indirectly via the private -
transferValue - variable):
*/
transferValue = protectedMemberOfB;
validTransfer = secret;
}
}
/* Null the - requestToGet - variable:
*/
requestToGet = null;
}
this.setProtectedMemberOfB = function(){
/* First ensure that the function is being executed as
a method of this object:
*/
if(this == self){
/* Check to see that the - requestToSet - variable
has been set to the correct secret:
*/
if(requestToSet == secret){
/* All is well so set the - protectedMemberOfB
- (indirectly from the private -
transferValue - variable):
*/
protectedMemberOfB = transferValue;
}
}
/* Null the - requestToSet - variable:
*/
requestToSet = null;
}
this.incrementProtectedMemberOfB = function(){
/* First ensure that the function is being executed as
a method of this object:
*/
if(this == self){
/* Check to see that the - requestToExecute -
variable has been set to the correct secret:
*/
if(requestToExecute == secret){
/* All is well, so act: */
protectedMemberOfB++;
}
}
/* Null the - requestToExecute - variable:
*/
requestToExecute = null;
}
this.passOnProtMemOfBToClassAInst = function(classAinst){
/* First ensure that the function is being executed as
a method of this object and that the object passed
by reference has the required method:
*/
if((this == self)&&(classBinst.setProtectedMemberOfA)){
/* Set up the interaction with the instance of
ClassA by assigning - secret - to the -
requestToSet - variable:
*/
requestToSet = secret;
/* The place the value of - protectedMemberOfB - in
a location that is accessible to instances of
the appropriate classes:
*/
transferValue = protectedMemberOfB;
/* Call the protected setter on the instance of
ClassA:
*/
classAinst.setProtectedMemberOfA();
/* Null the - requestToSet - variable:
*/
requestToSet = null;
}
}
this.setProtMemOfBToClassAInstProM = function(classAinst){
/* First ensure that the function is being executed as
a method of this object and that the object passed
by reference has the required method:
*/
if((this == self)&&(classBinst.getProtectedMemberOfA)){
/* Set up the interaction with the instance of
ClassA by assigning - secret - to the -
requestToGet - variable:
*/
requestToGet = secret;
/* Call the protected getter on the instance of
ClassA:
*/
classAinst.getProtectedMemberOfA();
/* If the call was to a valid getter the -
requestToGet - variable should have been nulled,
and successful data transfer will be marked by
the - validTransfer - variable knowing the
secret:
*/
if((!requestToGet)&&(validTransfer == secret;)){
/* Assign the value of the - transferValue -
variable to the local protected member:
*/
protectedMemberOfB = transferValue;
}
/* Null the - requestToGet - variable: */
requestToGet = null;
/* Null the - validTransfer - variable: */
validTransfer = null;
}
}
}
return constr;
})();
})();

Thus only objects constructed as instances of ClassA and ClassB are able
to interact in certain ways, emulating a concept of "protected". As you
can see the effort to implement is considerable and probably not worth
the gains (if any). I can live without this particular aspect of OO,
after all we will not be implementing aircraft fly-by-wire systems in
javascript, or anything so complex that the simpler emulations of
private instance and static members won't provide all the data hiding
that is needed.

Richard.
 
M

Michael Winter

I was about to say "me too", but found that it can be downloaded
at <URL:http://www.mozilla.org/js/language/> (along with version 1).

Thank you, both of you.

It's strange that the earlier editions aren't available from the ECMA
website, or that changes between versions aren't marked.

I do have the 3rd edition, but I can rarely make heads or tails of it. It
must be the worst written, yet grammatically-correct document I've ever
read. I find it odd that that level of incomprehensibility is acceptable
to the authors. I would have thought that the aim of the document would be
to explain the language to anyone who wishes to use it, as other language
specifications attempt to do. I'm not too sure who that document is geared
towards. It certainly isn't at a level that the casual script author can
use. Compare it to the Java Language Specification, for example.

Richard: Thank you for your explanation of protected members in
JavaScript, and for the comments on my proposed approach. I guessed that
there would be problems, but I didn't think that it would be with the
availability of instanceof (it was introduced in v1.4 with exceptions). I
won't be able to review the code you presented tonight, but I will
tomorrow.

Mike
 
L

Lasse Reichstein Nielsen

Michael Winter said:
I do have the 3rd edition, but I can rarely make heads or tails of
it. It must be the worst written, yet grammatically-correct document
I've ever read.

I have seen worse :)
I find it odd that that level of incomprehensibility
is acceptable to the authors. I would have thought that the aim of the
document would be to explain the language to anyone who wishes to use
it, as other language specifications attempt to do.

I disagree. The purpose of a language specification is to define the
language unambiguously and exactly. It is not a tutorial, it is a
reference document. Adding too much explanation text risks adding
ambiguiuity either in the text or between the text and the formal
specification.

Ofcourse, there should also be a tutorial, but I guess ECMA left that
to the Netscape and Microsoft Corporations or other implementors of
the language.
I'm not too sure who that document is geared towards. It certainly
isn't at a level that the casual script author can use.
Compare it to the Java Language Specification, for example.

Ok, ECMAScript is not exactly easy to read :) I admit that the JLS
is easier to read, although it too can get pretty hard too.
Is it also as complete, including the behavior of all build-in
functions?

Now compare it to the R5RS
<URL:http://www.swiss.ai.mit.edu/~jaffer/r5rs_toc.html>

/L
 
M

Michael Winter

I have seen worse :)

I don't doubt that.
I disagree. The purpose of a language specification is to define the
language unambiguously and exactly. It is not a tutorial, it is a
reference document. Adding too much explanation text risks adding
ambiguiuity either in the text or between the text and the formal
specification.

It would depend on who is the target audience. The Standard doesn't say,
but it does seem that, due to the detail in how methods are implemented,
writers of a JavaScript interpreter are the most likely.
Ofcourse, there should also be a tutorial, but I guess ECMA left that
to the Netscape and Microsoft Corporations or other implementors of
the language.

I don't find that the Netscape and Microsoft references are always
sufficient. For the most part, they are fine, but I would like something
between them and ECMA-262.
Ok, ECMAScript is not exactly easy to read :) I admit that the JLS
is easier to read, although it too can get pretty hard too.
Is it also as complete, including the behavior of all build-in
functions?

I can't say for certain. It has been a year since I read it properly (I
went front-to-back then). However, if memory serves, the grammar of the
language is fully specified. Multi-threading issues are covered to a level
where a conforming implementation could be built. I think even the loading
of class files is covered.

Perhaps the difference between them is that Sun provide the run-time
engine for virtually every operating system, so no implementations need to
be made. That allows the specification to concentrate on language itself,
and not its internal behaviour. If ECMA-262 contained some additional
comments that summarise the processes described, it would be a good step
forward. This would allow both precision and comprehension.

In what regard: specificity or ease of reading?

With the former, it appears to be about equal to JLS. Somewhat more
verbose, perhaps, but not radically different.

With the latter, I can't honestly say without reading and understanding a
good proportion of the text which I haven't had the time to do, and have
no inclination to do at this time of night :) Superficially, it is easier
to comprehend than ECMA-262.

The two major stumbling blocks I have are the indirect method of
description, and the choice of language. The specification seems to be
written with an incremental approach, where parts of an explanation rely
heavily on previous topics (the definition of some basic functions, for
example). This gives the impression that one must virtually memorise the
first few sections before even considering the use of the remaining
document, which makes it useless (for me) for a reference. The language is
a minor problem in comparison. To illustrate that, the only example I
could find after a quick search is the use of "Disjunction" instead of
"Separator" in the grammar for regular expression patterns.

Perhaps, at some point, I should read the whole thing slowly, rather than
to try and use it to understand certain elements of the language.

Mike
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top