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.