"with" statement, extending prototype

E

eman1000

I was recently looking at the prototype library
(http://prototype.conio.net/) and I noticed the author used the
following syntax:

Object.extend(MyObj.prototype, {
my_meth1: function(){},
my_meth2: function(){}
});

to define new methods on the MyObj prototype object. Object.extend
simply merges the two objects passed in. In this case MyObj.prototype
is our empty prototype object and the object literal is what we are
merging in. I really like this approach over the standard:

MyObj.prototype.my_meth1 = function(){};
MyObj.prototype.my_meth2 = function(){};

I like it because it allows me to use object initializers to define my
object (which is shorter, less error prone and keeps my code DRY) but
it doesn't squash any already existing properties on the object (kind
of feels like the open classes feature in Ruby). The only thing I don't
like about it is the need for using a function to do the merge. So I
got thinking of an alternate implementation that doesn't have this
problem but still has the same advantages. What if we did:

with(MyObj.prototype) {
function my_meth1(){}
function my_meth2(){}
}

It is my understanding that

function myfunc() {
}

is the exact same as

var myfunc = function(){}

which is the exact same as:

<current scope object>.myfunc = function() {}

Of course most of the time you can't access the top item on the scope
chain but that is what I understand to be happening behind the scenes.
Since the "with" statement puts my own object at the top of the scope
chain it seems that any functions defined in the with block would cause
a new property to be create on the specified object. This should allow
me to do what I want without using any special function (just builtin
stuff).

So I decided to try this experiment and it seemed to fail. I define my
object as follows:

var Car = function() {}
with(Car.prototype) {
function start() {
alert('starting');
}
}
mycar = new Car();
mycar.start();

This gives me an error about "start" not being defined on mycar. I
never get any errors with the with statement. It just seems that
functions defined in the with block don't get added to the Car
prototype object.

Anybody got any suggestions or ideas. I tested this theory under
Firefox.

Eric
 
R

RobG

(e-mail address removed) said on 05/04/2006 1:23 PM AEST:
I was recently looking at the prototype library
(http://prototype.conio.net/) and I noticed the author used the
following syntax:

Object.extend(MyObj.prototype, {
my_meth1: function(){},
my_meth2: function(){}
});

to define new methods on the MyObj prototype object. Object.extend
simply merges the two objects passed in. In this case MyObj.prototype
is our empty prototype object and the object literal is what we are
merging in. I really like this approach over the standard:

MyObj.prototype.my_meth1 = function(){};
MyObj.prototype.my_meth2 = function(){};

I like it because it allows me to use object initializers to define my
object (which is shorter, less error prone and keeps my code DRY) but
it doesn't squash any already existing properties on the object (kind
of feels like the open classes feature in Ruby). The only thing I don't
like about it is the need for using a function to do the merge. So I
got thinking of an alternate implementation that doesn't have this
problem but still has the same advantages. What if we did:

with(MyObj.prototype) {
function my_meth1(){}
function my_meth2(){}
}

It is my understanding that

function myfunc() {
}

is the exact same as

var myfunc = function(){}

Not *exactly* the same, the first is a function declaration, the second
is a function expression. With an expression, the name (identifier) is
optional, so a closer match is:

var myfunc = function myfunc(){};

One difference is that Gecko's JS engine assigns a name attribute to the
function's arguments object. If the function is anonymous, the name
attribute is empty. Not so for a function declaration, which must have
an identifier (IE doesn't provide an arguments.name property).

There seems little point to using a function expression if none of its
specialness is being used - e.g. conditional evaluation:

var someFunc;
if (...){
someFunc = function(){ /* body 1 */ };
} else {
someFunc = function(){ /* body 2 */ };
}

which is the exact same as:

<current scope object>.myfunc = function() {}

Where 'current scope object' is what, an object? It is equivalent to
assigning an anonymous function to the myfunc property of the object.

Of course most of the time you can't access the top item on the scope
chain

Isn't the 'top item' always the global object? Or do you mean 'this'?

but that is what I understand to be happening behind the scenes.
Since the "with" statement puts my own object at the top of the scope

Not so much 'at the top' but before the scope chain of the current
execution context.

chain it seems that any functions defined in the with block would cause
a new property to be create on the specified object. This should allow
me to do what I want without using any special function (just builtin
stuff).

So I decided to try this experiment and it seemed to fail. I define my
object as follows:

var Car = function() {}
with(Car.prototype) {
function start() {
alert('starting');
}
}
mycar = new Car();
mycar.start();

This gives me an error about "start" not being defined on mycar. I

Not exactly. It gives the error 'mycar.start is not a function', which
I guess is pretty much equivalent. :)

never get any errors with the with statement. It just seems that
functions defined in the with block don't get added to the Car
prototype object.

No errors because it 'works'. The function does get added, but as a
private member, not public - Mike Winter or someone can likely explain
how that works, not me!. You have effectively done:

var Car = function(){
function start(){
alert('starting');
}
}


What you need to do now is add a privileged method to call start():

Car.prototype.callStart = function(){start()};
var aCar = new Car();
aCar.callStart(); // shows 'starting'


Now it 'works', but it's not worth recommending just to satisfy some
coding preference - especially since it requires a non-preferred
statement to make it work, which defeats the purpose.

To use 'with' and keep start() public, you can use:

with (Car){
prototype.start = function(){alert('starting');};
}

but that just seems like a bad way to do:

Car.prototype.start = ...;

Anybody got any suggestions or ideas. I tested this theory under
Firefox.

Me too, but its the same for IE.
 
E

eman1000

First I want to thank you for your feedback. I am one of the waves of
programmers who misunderstood Javascript for many years until the
recent development of Ajax libraries (such as prototype) have showed me
just how cool Javascript can be. I'm still trying to understand all the
details so I greatly appreciate any corrections to my understanding.
Where 'current scope object' is what, an object? It is equivalent to
assigning an anonymous function to the myfunc property of the object.

<current scope object> referred to the last item added to the scope
chain. I thought identifiers created in the current scope were really
properties of the last item added to the scope chain. Maybe this is my
misunderstanding.
Isn't the 'top item' always the global object? Or do you mean 'this'?

By "top item" I meant the last item added to the scope chain (I am
thinking about it like a stack with objects being popped on and off the
scope chain as you enter and exit functions).
No errors because it 'works'. The function does get added, but as a
private member, not public - Mike Winter or someone can likely explain
how that works, not me!.

Hmmm.... I'm not sure I understand this private vs public member stuff.
Will have to do some reading on it. Thanks for pointing me in the right
direction.
To use 'with' and keep start() public, you can use:

with (Car){
prototype.start = function(){alert('starting');};
}

I'm not sure I understand why:

with (Car) {
prototype.start = function() {alert('starting');};
}

isn't equivilant to:

with (Car.prototype) {
start = function() {alert('starting');};
}

Once again thanks for your feedback,

Eric
 
J

Julian Turner

(e-mail address removed) wrote:

[snip]
I'm not sure I understand why:

with (Car) {
prototype.start = function() {alert('starting');};
}

isn't equivilant to:

with (Car.prototype) {
start = function() {alert('starting');};
}
[/snip]

In rough terms, when the "with" statement finds an "identifier", e.g.
"prototype" or "start", within the { ] it tries to find a corresponding
property of the object supplied to the with statement. But what it
does not do, is create a new property of the object, if it cannot find
one.

So:-

with (Car) {
prototype.start = function() {alert('starting');};
}

works, because "prototype" is an existing property of Car.

So it becomes the equivalent of "Car.prototype.start=", which is a
JavaScript valid way of creating a new property named "start".

and

with (Car.prototype) {
start = function() {alert('starting');};
}

does not work, because "start" is not an existing property of
"prototype", and there is no way that JavaScript can guess that you
want to create a new property. For all JavaScript knows, "start" could
equally be a previously declared variable or a new global variable you
are wanting to assign a value to.

The effect of the above code is to create a new "global" variable
called "start", and Car.prototype remains unchanged.

Regards

Julian Turner
 
L

Luke Matuszewski

(e-mail address removed) napisal(a):
var Car = function() {}
with(Car.prototype) {
function start() {
alert('starting');
}
}

Here is self explanatory example of public/protected paradigim:

function Car() {
function privateFunction()
{

}

this.publicFunction = function()
{

};
}

Car.staticConstructorFunction = function()
{

};

Car.prototype.sharedByAllCarObjectsFunction = function()
{

};

var car1 = new Car();

Car.prototype.start();
car1.start();

, still you can read about it here:

http://jibbering.com/faq/faq_notes/closures.html
http://www.litotes.demon.co.uk/js_info/private_static.html
http://www.crockford.com/javascript/private.html
http://www.crockford.com/javascript/inheritance.html

Greets
Luke M.
 
T

Thomas 'PointedEars' Lahn

RobG said:
(e-mail address removed) said on 05/04/2006 1:23 PM AEST:
[...]
It is my understanding that

function myfunc() {
}

is the exact same as

var myfunc = function(){}

Not *exactly* the same, the first is a function declaration, the second
is a function expression. With an expression, the name (identifier) is
optional, so a closer match is:

var myfunc = function myfunc(){};

One difference is that Gecko's JS engine assigns a name attribute to the
function's arguments object. If the function is anonymous, the name
attribute is empty. Not so for a function declaration, which must have
an identifier (IE doesn't provide an arguments.name property).

`name' attribute of the arguments object? What the heck are you talking
about?


PointedEars
 
L

Luke Matuszewski

(e-mail address removed) napisal(a):
var Car = function() {}
with(Car.prototype) {
function start() {
alert('starting');
}
}

Here is self explanatory example of public/protected paradigim:

function Car() {
function privateFunction()
{

}

this.publicFunction = function()
{

};
}

Car.staticConstructorFunction = function()
{

};

Car.prototype.sharedByAllCarObjectsFunction = function()
{

};



Car.prototype.start = function()
{
alert('starting');
};


var car1 = new Car();

Car.prototype.start();
car1.start();

, still you can read about it here:

http://jibbering.com/faq/faq_notes/closures.html
http://www.litotes.demon.co.uk/js_info/private_static.html
http://www.crockford.com/javascript/private.html
http://www.crockford.com/javascript/inheritance.html

Greets
Luke M.
 
V

VK

Thomas said:
`name' attribute of the arguments object? What the heck are you talking
about?

About Mozilla's proprietary extension of callee object. It is not
supported by IE.

Try:

<html>
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1">

</head>

<body onLoad="alert(arguments.callee.name);">

</body>
</html>
 
R

RobG

Thomas said:
RobG wrote: [...]
One difference is that Gecko's JS engine assigns a name attribute to the
function's arguments object. If the function is anonymous, the name
attribute is empty. Not so for a function declaration, which must have
an identifier (IE doesn't provide an arguments.name property).


`name' attribute of the arguments object? What the heck are you talking
about?

Ooops, I meant arguments.callee.name.
 
L

Lasse Reichstein Nielsen

RobG said:
Ooops, I meant arguments.callee.name.

I.e., a "name" property on the function object. It has nothing to
do with the arguments object.

It is indeed not in the ECMAScript standard.

/L
 
E

Eric Anderson

Julian said:
In rough terms, when the "with" statement finds an "identifier", e.g.
"prototype" or "start", within the { ] it tries to find a corresponding
property of the object supplied to the with statement. But what it
does not do, is create a new property of the object, if it cannot find
one. [...snip example...]
and there is no way that JavaScript can guess that you
want to create a new property. For all JavaScript knows, "start" could
equally be a previously declared variable or a new global variable you
are wanting to assign a value to.

I took a look at the EMCAScript standard again because this still did
not make sense. Javascript would have to be able to create properties
on whichever object existed at the front of the scope chain. Identifier
resolution is done by searching the scope chain (see section 10.1.4)
and identifiers created in the current execution context would have to
be added to the object at the front so that they are removed when that
execution context ends (i.e. when the object in the front of the scope
chain is removed).

But when looking at the standard I think I did find the answer. I
forgot that Javascript only has three types of execution contexts
(global, eval, and function). When it enters the execution context it
finds all variables declarations and function declarations in that
execution context and assignes them as properties to the variables
object (which is also the activation object, which is the object that
is put on the front of the scope chain when the execution context is
started). See 10.1.3 for more info on this.

This assignment of properties when entering the execution context
includes variables and functions declared in a "with" statement.
Javascript does not create a new execution context when entering the
"with" statement and therefore the functions and variables declared in
the "with" statement are a property of the execution context's
"variable" object.

Based on this understanding the following (in global context):

function Car() {}
with(Car.prototype) {
function start() {
alert('starting');
}
var foo = 'bar';
}

is equivilant to this (again in global context):

// Behind the scenes
this.Car = function Car() {};
this.start = function start() { alert('starting'); }
this.foo = undefined;

// Executing user code
with( Car.prototype ) {
foo = 'bar';
}

If the above code executed in a function the identifiers "Car", "start"
and "foo" would be properties of the function's variable object and
therefore once the function returned they would be garbage collected
and not even exist (unless of course a closure referenced that scope
chain therefore causing them to not be garbage collected).

Asssuming my understanding is correct I now understand why my
experiment did not work. Once again I want to say thanks for everyone's
help in making this clear.

Eric
 
T

Thomas 'PointedEars' Lahn

Eric said:
I took a look at the EMCAScript standard again because this still did
not make sense. Javascript would have to be able to create properties
on whichever object existed at the front of the scope chain.

No. First, the _ECMAScript_ implementation implemented in Mozilla Firefox
is called _JavaScript_. Second, what you say is the case only if the
object is a native object. If you read the spec thoroughly, you will
come to recognize that host objects do not need to implement built-in
ECMAScript methods like [[Put]] as they are specified; so there is no
requirement that they allow the creation of new properties.

That is why it is error-prone to use host object as targets for assignments
if a property would need to be created in the process. (One of the many
flaws of the Prototype library, which attempts to augment the host object
referred to with `document'.)


PointedEars
 
R

Richard Cornford

Eric said:
Julian said:
In rough terms, when the "with" statement finds an
"identifier", e.g. "prototype" or "start", within the
{ ] it tries to find a corresponding property of the
object supplied to the with statement. But what it does
not do, is create a new property of the object, if
it cannot find one.

The evaluation of the PrimaryExpression - Identifier - _always_ results
in an instance of the internal - Reference - type; an object with a
'base' property that either refers to an Object or is null, and a
'property' property that is a string that corresponds with the
Identifier (ECMA 262, 3rd Ed. Section 8.7; "The Reference Type" and
section 10.1.4 "Scope Chain and Identifier Resolution")[1]. For an
expression to add a property to, or modify a property off, an object
(other than the global object under some circumstances), using an
Identifier, the resulting Reference type must have its 'base' property
assigned a reference to that object.

If an object added to the scope chain with a - with - statement does not
have a property with a name that corresponds to an Identifier then the
process of resolving that Identifier against the scope chain will not
find such a property on that object and it will move on to the next
object in the scope chain. Once the process has moved on past the object
the resulting Reference type will _never_ have that object as its 'base'
property and so no assignment operations using the Identifier (or the
Reference type that the Identifier evaluates as) will be able to modify
that object's properties.
[...snip example...]
and there is no way that JavaScript can guess that you
want to create a new property.

It doesn't need to guess in reality as if you want to add a property to
an object added to the scope chain with a with statement you can use
whichever expression was used in the - with - statement as the left hand
side of a property accessor and add the property to the object
explicitly. I.E:-

with (Car.prototype) {
Car.prototype.start = function() {alert('starting');};
}

Of course the - with - statement becomes redundant in that case, and
merely a source of confusion if Identifiers are then used to read
properties of - Car.prototype - in the same - with - block. But then -
with - statements are known for their ability to result in unclear and
confusing code, to the extent that their use is strongly discouraged by
many (some say 'nerve use with', and I would only use it explicitly
modify the internal [[Scope]] property of a function created inside a -
with - statement).

- On any object further down the scope chain.

Although it is widely considered 'best practice' to never dynamically
create global variable, but instead to explicitly declare all global
variables used 'up front'.
I took a look at the EMCAScript standard again because this
still did not make sense. Javascript would have to be able
to create properties on whichever object existed at the front
of the scope chain.

It is impossible to dynamically add properties to an Activation/Variable
object (except with a call to - eval -) because it is not possible to
acquire a reference to one of those objects in order to carry out an
assignment that would create a new property on such an object. But any
other object that may be added to a scope chain may be modified as the
ability to add it to a scope chain implies an ability to reference the
object by some other means.
Identifier resolution is done by searching the scope chain
(see section 10.1.4) and identifiers created in the current
execution context

It would be better to talk of 'declaring' Identifiers (or better 'local
variables') as Identifiers are not something that can be 'created'
(except maybe possibly through the misguided use of - eval -).
would have to be added to the object at the front so that
they are removed when that execution context ends (i.e.
when the object in the front of the scope chain is removed).

But when looking at the standard I think I did find the
answer. I forgot that Javascript only has three types of
execution contexts (global, eval, and function). When it
enters the execution context it finds all variables
declarations and function declarations in that execution
context and assignes them as properties to the variables
object (...

That is; variable instantiation happens _prior_ to the execution of code
in all execution contexts.
This assignment of properties when entering the execution
context includes variables and functions declared in a
"with" statement.

The evaluation of the PrimaryExpression - Identifier - _always_ results
in an instance of the internal - Reference - type; an object with a
'base' property that either refers to an Object or is null, and a
'property' property that is a string that corresponds with the
Identifier (ECMA 262, 3rd Ed. Section 8.7; "The Reference Type" and
section 10.1.4 "Scope Chain and Identifier Resolution")[1]. For an
expression to add a property to, or modify a property off, an object
(other than the global object under some circumstances), using an
Identifier, the resulting Reference type must have its 'base' property
assigned a reference to that object.

If an object added to the scope chain with a - with - statement does not
have a property with a name that corresponds to an Identifier then the
process of resolving that Identifier against the scope chain will not
find such a property on that object and it will move on to the next
object in the scope chain. Once the process has moved on past the object
the resulting Reference type will _never_ have that object as its 'base'
property and so no assignment operations using the Identifier (or the
Reference type that it evaluates as) will be able to modify that
object's properties.

Well, strictly speaking you cannot declare a function 'within' a -
with - statement in ECMAScript. The productions Function Declaration and
Statement are the two productions that may make up SourceElement
(section 14) and (via SourceElements) the only components that make up
Program and FunctionBody. But because they are distinct it is not
possible for a Statement to contain a FunctionDeclaration, that would be
a syntax error in a pure ECMA 262 implementation.

Reality differs slightly from this 'a Function Declaration within a
Statement is a syntax error' situation for two reasons: in IE (JScript)
and Opera (and probably others) a Function Declaration within a
Statement is just treated as a normal FunctionDeclaration and the syntax
error is ignored, and in Mozilla/Gecko the ECMAScript implementation has
a FunctionStatement extension (which is allowed), and as a Statement it
is allowed to appear inside other Statements. However, with strict
formal correctness making a FunctionDeclaration inside a Statement a
syntax error, and two known alternatives available in the wild, actually
placing a (or what ECMA 262 would consider a) Function Declaration
inside a Statement has at lest three variable outcomes and so would be
something that someone wanting to write cross-browser code would _never_
do.

Of course this is not an issue in reality because ECMAScript provides
FunctionExpression, and that may appear inside Statements, allowing the
conditional and context-sensitive creation of function objects on
demand.
Javascript does not create a new execution context when
entering the "with" statement and therefore the functions
and variables declared in the "with" statement are a property
of the execution context's "variable" object.

Based on this understanding the following (in global context):

function Car() {}
with(Car.prototype) {
function start() {
alert('starting');
}
var foo = 'bar';
}

- is a syntax error in a pure, strict ECMA 262 implementation, a
FunctionStatement in implementations that have that extension and a
Function Declaration in an unusual context in implementations that don't
have a FunctionStatement but disregard the Syntax error.
is equivilant to this (again in global context):

// Behind the scenes
this.Car = function Car() {};
this.start = function start() { alert('starting'); }
this.foo = undefined;

// Executing user code
with( Car.prototype ) {
foo = 'bar';
}

They are only (very approximately) equivalent where no FunctionStatement
extension is implemented and the syntax error is ignored (ie, IE and
Opera for certain). I don't know if the Mozilla/Gecko FunctionStatement
extension is documented to the extent that it would be possible to know
whether the resulting function's internal [[Scope]] property will
include - Car.prototype - in its scope chain or not, but if it does (and
I expect it would) then they are certainly not equivalent in that
environment.
If the above code executed in a function the identifiers
"Car", "start" and "foo" would be properties of the
function's variable object ...
<snip>

The value of the - this - keyword has no relationship to an execution
context's Activation/Variable object so the above does not appear to
make sense.
Asssuming my understanding is correct I now understand
why my experiment did not work.
<snip>

That variable instantiation happens prior to the execution of function
body code? That is the correct explanation.

Richard.

[1] Because the - Reference - type is internal and a 'specification
mechanism' that is used to define the behaviour required of an
ECMAScript implementation, it may be going too far to call it an object
with specific properties. Inside any instance of code that implements
ECMAScript there may be concrete equivalent of a 'Reference' type,
and/or it may not be an 'object'. However, in attempting to understand
how ECMAScript implementations behave it strikes me as most direct,
appropriate and productive for the student to perceive the Reference
type as an object with 'base' and 'property' properties.
 
R

Richard Cornford

Richard said:
The evaluation of the PrimaryExpression -
... that object.

If an object ... properties.

The evaluation of the PrimaryExpression -
... that object.

If an object ... properties.

The repletion of these two paragraphs is a cut-and-paste error. They can
be ignored in this context. The following paragraph is the start of my
response to the preceding text.
Well, strictly ...
<snip>

Richard.
 

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,989
Messages
2,570,207
Members
46,782
Latest member
ThomasGex

Latest Threads

Top