with and prototype

R

Robert

Hi,

I can use "with" like this:

function MyObject(message)
{
this.message = message;
}
function _MyObject_speak()
{
alert(this.message);
}

with (MyObject)
{
prototype.speak = _MyObject_speak;
}


I was wondering why I can't use "with" like this:

with (MyObject.prototype)
{
speak = _MyObject_speak;
}
 
W

web.dev

Robert said:
Hi,

I can use "with" like this:

function MyObject(message)
{
this.message = message;
}
function _MyObject_speak()
{
alert(this.message);
}

with (MyObject)
{
prototype.speak = _MyObject_speak;
}


I was wondering why I can't use "with" like this:

with (MyObject.prototype)
{
speak = _MyObject_speak;
}

Hi Robert,

When you use with(MyObject), you're accessing properties of MyObject
within that scope. Therefore, extending MyObject is valid.

However, when you use with(MyObject.prototype), you're accessing
properties of MyObject.prototype within that scope. Therefore, you're
not performing extensions to your MyObject.

:) So you just misunderstood the behavior of the with statement.

Hope this helps.
 
R

Richard Cornford

Robert wrote:
I can use "with" like this:

Be aware that the - with - statement is a part of javascript that some
informed opinion would like to see eliminated from the language
entirely, resulting in its use being strongly discouraged.

I don't hold such a strong opinion on the subject, but I find that I
have little use for the - with - statement. There is one interesting use
for it in deliberately augmenting the scope chains of function
expressions evaluate in a - with - statement (an alternative to using
closures, or a different sort of closure, depending on how you look at
it). But that is very far from an everyday requirement (and there are
issues with older language implementations) and apart from that the use
of - with - statements just seems to render source code obscure.

The supporters of the - with - statement point out that it can reduce
script size (and/or typing), but there are other ways of doing that
(with at least equally obscure results).
function MyObject(message)
{
this.message = message;
}
function _MyObject_speak()
{
alert(this.message);
}

with (MyObject)
{
prototype.speak = _MyObject_speak;
}

You can do that, but I don't see any reason for doing so. Assigning
methods to prototypes is clearly and easily achieved with statements
such as:-

MyObject.prototype.speak = function(arg1, arg2){
... //function body
};

- or entire prototypes compactly defined as:-

MyObject.prototype = {
speak:function(arg1, arg2){
... //function body
},
act:function(){
... //function body
},
... // etc.
};
I was wondering why I can't use "with" like this:

The - with - statement acts by evaluating its expression
(MyObject.prototype in this case,) type-converting the result into an
object (no conversion is necessary if the expression resolves as an
object, which is expected in the case of - MyObject.prototype -), and
then adding that object to the top of the current execution context's
scope chain for the duration of the evaluation of the statement that
follows (which may be, and usually would be, a Block statement
containing other statements).

Unqualified Identifiers are resolved against the scope chain of the
current execution contexts (ECMA 262 3rd edition; section 10.1.4). The
object at the top of the scope chain is examined to see if it has a
property with a name that corresponds with the Identifier. If it does
then the Identifier is resolved as a reference to that object using the
name that was found.

In the terms of the specification; an instance of the internal Reference
type is returned that has the object as its 'base object' and the
corresponding property name as its 'property name' (ECMA 262, 3rd
edition; section 8.7). This Reference type instance is used to target
assignment ( [[Put]] ) operations and to source retrieval ( [[Get]] )
operations, and is an internal intermediate result used in resolving
expressions in place of the original Identifier.

If the object does not have a property with a corresponding name then
the next object in the scope chain undergoes a similar examination. This
goes on until either one of the objects on the scope chain is found with
a correspondingly named property, or the scope chain comes to an end
(scope chains are always finite by specification).

If Identifier resolution against the scope chain cannot resolve an
Identifier (it gets to the end of the scope chain without finding a
property with the corresponding name on any of the objects in the chain)
then a Reference type is returned that has a null 'base object' (the
property name still corresponds with the Identifier).

When a Reference type with a null 'base object' is used to target or
source an operation the global object is used in place of null. Thus any
unresolved Identifiers become references to non-existent properties of
the global object. (the global object is always the last object on any
scope chain so if the global object already had a property with the
corresponding name the scope chain resolution of the identifier would
succeed, resulting in a Reference type with the global object as its
'base object'. Though such a resolution is practically indistinguishable
from a Reference type that has a null 'base object').

So assigning to an unresolved, unqualified, Identifier results in the
creation of a new named property of the global object and the assignment
of the value to that property.
with (MyObject.prototype)
{
speak = _MyObject_speak;
}

This does not have the desired effect because - MyObject.prototype -
does not have a property named "speak". The Identifier "speak" is not
resolved as a named property of any object on the scope chain
(including - MyObject.prototype -) and so the assignment results in the
creation of a new property of the global object named "speak" and the
value is assigned to that property.

if - MyObject.prototype - did have a "speak" property, e.g.:-

MyObject.prototype = {
speak:null
};

- then:-

with(MyObject.prototype){
speak = _MyObject_speak;
}

- would have succeeded, as the scope chain resolution of the Identifier
"speak" would find that the - MyObject.prototype - object at the top of
the scope chain did have a correspondingly named property and so the
intermediate Reference type would have - MyObject.prototype - as its
'base object' and "speak" as its 'property name'. The value would be
assigned to - MyObject.prototype.speak -.

It is at least in part this unobvious behaviour, in comparison to:-

MyObject.prototype.speak = _MyObject_speak;

- and the like, that encourages the deprecating attitude towards the -
with - statement.

For completeness; the issue that I mentioned above with older
implementations is that the scope chain resolution of Identifiers is
specified (in ECMA 262 3rd edition (only)) as including the prototype of
objects on the scope chain. I.E. an object on the scope chain is
considered to have a correspondingly named property if it, or its
prototype, or its prototype's prototype (and so on) has a property with
the name. Older implementations (Netscape 4 is a specific example) do
not necessarily consider the prototypes in scope chain resolution of
Identifiers. Thus:-

with(MyObject.prototype){
toString = _alternative_toString;
}

- will assign a - toString - method to - MyObject.prototype - on an ECMA
262 3rd edition implementation because -
MyObject.prototype.prototype.toString - exists (the - toString - method
of - Object.prototype -), but Netscape 4 instead assigns the method to
the global object, oblivious to its - Object.prototype.toString -.

Richard.
 
R

Richard Cornford

Richard Cornford wrote:
When a Reference type with a null 'base object' is used to
target or source an operation the global object is used in
place of null.
<snip>

That isn't strictly true. A Reference type with a null 'base object'
will throw an exception when used to source a value, only assignment
uses the global object in place of null.

Richard.
 
R

Robert

web.dev said:
However, when you use with(MyObject.prototype), you're accessing
properties of MyObject.prototype within that scope. Therefore, you're
not performing extensions to your MyObject.

:) So you just misunderstood the behavior of the with statement.

Yes, you are right. I misunderstood :)
 
R

Robert

Richard said:
Robert wrote:



So assigning to an unresolved, unqualified, Identifier results in the
creation of a new named property of the global object and the assignment
of the value to that property.

Thanks, for your explanation! I understand now.

MyObject.prototype = {
speak:function(arg1, arg2){
... //function body
},
act:function(){
... //function body
},
... // etc.
};

That's an interesting alternative.
I never really use -with-, but I will keep this in mind if I feel the
urge to use it again.
 
R

Richard Cornford

Robert said:
Richard Cornford wrote:

That's an interesting alternative.
<snip>

It is a nicely clear, self-contained and compact approach to defining a
prototype. However, it is worth being aware that it has consequence that
will probably not be obvious to many. When a function object is created
a prototype object is created for that function. This prototype object
is given a property called "constructor" to which a reference to the
function is assigned. So if the function is used as a constructor for
new object instances they will all inherit a - constructor - property
through their prototype, that refers back to the function that
constructed them.

I have never yet found any practical use for this - constructor -
property. It can be used to allow scripts to determine the 'type' of an
object instance at runtime. My thoughts are that if the 'type' of an
object instance is at issue at runtime (and cannot be easily determined
by some other means) then there is a code design problem that should be
addressed before considering the object's - constructor - property.
However, the property would exist on all normal object instances.

The act of replacing the function object's prototype with an alternative
object consequently results in the - constructor - property being
inherited from that object's prototype. In the case of using an object
literal as the prototype the - constructor - property is inherited
from - Object.prototype -, and will be a reference to the - Object -
function.

On the plus side the - constructor - property of function prototypes is
not read only (and an inherited - constructor - property can be masked
on the object itself anyway), and "constructor" is not a keyword or
reserved word in the language. So the replaced prototype can provide its
own 'constructor' property if one is likely to be needed:-

MyObject.prototype = {
constructor:MyObject,
speak:function(arg1, arg2){
... //function body
},
act:function(){
... //function body
},
... // etc.
};

Though I wouldn't bother with that myself.

Richard.
 
M

Michael Winter

On 26/08/2005 02:37, Richard Cornford wrote:

[snip]
I have never yet found any practical use for this - constructor -
property. It can be used to allow scripts to determine the 'type' of an
object instance at runtime. My thoughts are that if the 'type' of an
object instance is at issue at runtime (and cannot be easily determined
by some other means) then there is a code design problem that should be
addressed before considering the object's - constructor - property.

I don't think that's fair.

With other languages, one can often enforce code usage by including the
type name with an argument thereby preventing client code accidentally
misusing a method. Similarly, one can overload methods with different
signatures. Though neither is natively supported by ECMAScript,
type-checking using the constructor function allows emulation (much like
closures allow emulation of other language features).

If client code is trusted, the former case (type enforcement) isn't
necessary, but latter can prove useful now and then.

[snip]

Mike
 
R

Robert

Richard said:
<snip>

It is a nicely clear, self-contained and compact approach to defining a
prototype. However, it is worth being aware that it has consequence that
will probably not be obvious to many. When a function object is created
a prototype object is created for that function. This prototype object
is given a property called "constructor" to which a reference to the
function is assigned. So if the function is used as a constructor for
new object instances they will all inherit a - constructor - property
through their prototype, that refers back to the function that
constructed them.

Aha, thank you for telling me this. I don't think it is a problem for
any code I have written so far, however I thought about using the
constructor when I write the types of the arguments of a function (used
when I want a stacktrace in an invalid function call).

The constructor would give me some additional information I did not get
out of it so far.

Would you consider this bad design?

BTW I really enjoy the expertise in this newsgroup.

Robert.
 

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,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top