Richard Cornford wrote:
"Inspired" by one of the more ambiguous questions on your
meebo.com page I thought the following might make quite
interesting written test questions, and give an impression
of my thought process in setting javascript questions:-
/* unknown global code */
function outerFunction(){
/* unknown outer function body code */
function innerFunction(){
/* unknown inner function body code */
with(anObjectReference){
x = 5; //<--- The subject line of code.
}
/* more unknown inner function body code */
}
/* more unknown outer function body code */
}
/* more unknown global code */
/* ********************************************************\
| Note: Three facts about the 'unknown' code:- |
| |
| 1. There are no more function definitions, no function |
| expressions and no uses of the Function constructor. |
| 2. There are no - with - statements in the unknown code.|
| 3. There are no uses of the - eval - function. |
\******************************************************** */
Q1: Assuming the line that reads - x = 5; - is executed, which
(group of) of the following are possible outcomes of its execution?
[My second question, quoted out of sequence:-]
| Q2: If the line of code above is changed from - x = 5; - to -
| var x = 5 - which (group of) the above are then the possible
| outcomes of the execution of that line?
At the point where the line of code is executed the - with - statement
has augmented the scope chain by adding an object to the top (or front,
depending on how you want to look at it) of it. Giving a scope chain
consisting of 4 objects; the object added with the - with - statement,
the Activation/Variable object from the execution context in which the -
x = 5; - line is executing, the Activation/Variable object associated
with the outer function when the inner function object was created, and
finally the global object.
The unknowns in this code allow any object to be the one added at the
top of the scope chain, and for any local variables to have been
declared in any function or globally, and so result in either
Activation/Variable object and the global object having any properties
when the - x = 5; - line is executed.
The procedure for resolving Identifiers such as - x - is to start at the
top of the scope chain and examine the object there to see if it has a
property with a name that corresponds with the Identifier. The nature of
this examination is to call the object's internal [[HasPropery]] method,
which, in the even that it cannot find a property on the object itself,
calls the [[HasProperty]] method of the object that is referred to by
its [[Prototype]] (if it is not null). Thus the test to see if the
object has a property with the given name is applied to the object and
all the objects on its prototype chain.
If the first object on the prototype chain does not have a property with
the corresponding name the testing moves to the next object in the
chain, and the test is repeated on that object, and so on until either
an object with a property with the corresponding name is found or the
scope chain comes to an end and there are no more objects to examine.
If an object on the scope chain (or one of its prototypes) is found to
have a property with the corresponding name the result of the resolution
of the Identifier is a Reference type with its 'base' property set to a
reference to the object on the scope chain, and its 'propertyName'
property set to the name of the property found (that is, the
Identifier).
If no object on the scope chain (or any of their prototypes) was found
to have a property with the corresponding name the result is again a
Reference type with its 'propertyName' property set to name that
corresponds with the Identifier, but this time the 'base' property is
set to null.
An assignment involves evaluating the expression to be assigned (to the
numeric value 5 in our case) and then resolving the left hand side of
the assignment into a Reference type (or generating a runtime error if
that is not possible) and then assigning the value of the right hand
expression to a property of the object referred to by 'base' property of
the Reference type with the name that is the Reference type's
'propertyName' value. If the Reference type's 'base' property is null
the global object becomes the substitute and it receives the assigned
value in one of its properties. And whenever the object (itself, not its
prototypes) does not already have a property with the corresponding name
one is created on it.
Thus the type of outcomes that are possible with - x = 5; - and - var x
= 5; - are the assignment of the value 5 to properties of objects and a
possible side effect of creating new properties of (some of) those
objects as a consequence. The subjects of the individual answers are
then which of these actions could possibly be applied to the various
objects that are of interest here, in the context of the code (both
specified and possible).
A quick listing of my answers, where 'P' indicates a 'Possible' and 'N'
a 'Not Possible':-
| x = 5 | var x = 5
----|---------|-----------
1 | P | P
2 | P | P
3 | P | P
4 | P | P
5 | P | P
6 | P | P
7 | N | N
8 | P | N <<- Changed by adding - var -.
9 | N | N
10 | P | P
11 | N | N
12 | P | P
13 | P | P
14 | P | P
15 | P | P
16 | P | P
17 | P | P
--------------------------
Some answers can be regarded as, as RobG suggested, splitting hairs, and
others may seem that way. However, with the exception of items such as
#7, where getting it wrong would be fatal, coming up with an answer that
differs from mine is neither unacceptable nor unexpected. My interest is
more in how the individuals react to being shown why they were wrong.
#1, #3 and #5 being examples of this. The question hangs on the
interpretation of the exact wording (the subject is always the object
itself not its prototypes) so it is very easy to miss the distinction
between the object as you would interact with it in code (where it
behaves as if it has all the properties of its prototype) and the object
as it really is. If someone says that the answers are 'not possible'
because they perceive that and object inheriting a property from a
prototype is equivalent to its having that property then that is a
viable and acceptable way of looking at the situation. My interest would
be in seeing whether, being shown how #1 is possible, they perceived the
implications for #3 and #5 immediately (and #13 and #15 in the - var x
= 5; - case, but more on that later).
(Though all credit to those who do spot the possibility, and so -
zeroglif@gmai ... - who posted the demonstration of the possibility
within a minute of my posting that there was a proof of the possibility,
and so independently of my post implying that looking for a proof might
be worth the effort).
So to the answers:-
1. The creation of an 'x' property of the 'outerFunction'
function and the assignment of the value 5 to that property.
x = 5; - As has been discussed, 'outerFunction' is a discrete object
that inherits properties from its prototypes (Function.prototype and
Object.prototype). If the - with - statement is used to put a reference
to it at the top of the scope chain the testing of that object to see if
it 'has' an 'x' property, using its internal [[HasProperty]] method,
will return true if the property is defined on either prototype but not
the object itself. Then the assignment would target the 'outerFunction'
object and create a property on that object as a side effect:-
Object.prototype.x = 0;
function outerFunction() {
function innerFunction() {
var anObjectReference = outerFunction;
alert(outerFunction.hasOwnProperty('x')); //false
with (anObjectReference) {
x = 7;
}
alert(outerFunction.hasOwnProperty('x'));//true
}
innerFunction();
}
outerFunction();
The - hasOwnProperty - method being a good indicator of whether the
object itself has the property in question.
#1 is possible.
var x = 5; - The situation is not altered by declaring a function local
variable for 'innerFunction' because the Variable object for
'innerFunction' is below whatever object the - with - statement added to
the scope chain and so when that added object becomes the target for the
assignment the scope chain resolution of the 'x' Identifier stops and so
never gets to innerFunctios' Variable object. #1 is possible.
2. The assignment of the value 5 to a pre-existing 'x' property
of the 'outerFunction' function.
x = 5; - If 'outerFunction' has an 'x' property and it is placed at the
top of the sc0pe chain by the - with - statement the assignment is going
to target that property of that object. #2 is possible.
var x = 5; - Again the situation is not altered by declaring a function
local variable for 'innerFunction'. #2 is possible.
3. The creation of an 'x' property of the 'innerFunction'
function and the assignment of the value 5 to that property.
x = 5; - 'innerFunction' and its prototypes can be subject to exactly
the same manipulations as 'outerFunction' and it can also be placed at
the top of the scope chain. #3 is possible.
var x = 5; - The situation is not altered by declaring a function local
variable for 'innerFunction'. #3 is possible.
4. The assignment of the value 5 to a pre-existing 'x' property
of the 'innerFunction' function.
x = 5; - Whatever is possible for 'outerFunction' is possible for
'innerFunction'. #4 is possible.
var x = 5; - The situation is not altered by declaring a function local
variable for 'innerFunction'. #4 is possible.
5. The creation of an 'x' property of the object referred to by
'anObjectReference' and the assignment of the value 5 to that
property.
x = 5; - if something is possible for 'outerFunction' as a result of
making it "the object referred to by 'anObjectReference'" it must also
be possible for the object referred to by 'anObjectReference'. #5 is
possible.
var x = 5; - The situation is not altered by declaring a function local
variable for 'innerFunction'. #5 is possible.
6. The assignment of the value 5 to a pre-existing 'x'
property of the object referred to by 'anObjectReference'.
x = 5; Same as #5. #6 is possible.
var x = 5; - The situation is not altered by declaring a function local
variable for 'innerFunction'. #6 is possible.
7. The creation of a local variable of the 'outerFunction'
function named 'x' and the assignment of the value 5 to
that variable.
x = 5; An assignment does not declare a variable. #7 is not possible
(getting this one wrong is fatal).
var x = 5; - Declaring a function local variable for 'innerFunction' is
not declaring a function local variable for 'outerFunction' (and that is
assuming that "the execution of that line" could result in "the
creation of a local variable" at all). #7 is not possible (getting this
one wrong is fatal).
8. The assignment of the value 5 to a declared local variable
of the 'outerFunction' function named 'x'.
x = 5; Yes, if 'outerFunction' included a declaration of a local
variable 'x' and no object higher on the scope chain had an 'x' property
then the assignment of the value 5 to that local variable is the
expected outcome. #8 is possible (getting this one wrong is fatal).
var x = 5; - Declaring a function local variable for 'innerFunction'
results in its Variable object having an 'x' property, and its variable
object is above that of 'outerFunction' on the scope chain so the
Identifier resolution can never get to the Variable object for
'outerFunction'. (it is not possible to reference any Variable object
except the global object, so the - with - statement cannot be used to
put the 'outerFunction' Variable object at the top of the scope chain.)
#8 is not possible (getting this one wrong is fatial).
9. The creation of a local variable of the 'innerFunction'
function named 'x' and the assignment of the value 5 to
that variable.
x = 5; An assignment does not declare a variable. #9 is not possible
(getting this one wrong is fatal).
var x = 5; - Strictly this is not possible because the questions ask
about the "outcomes of the execution of that line" and the local
variable that results for the variable declaration here was created
during variable instantiation for the execution context. That is, the
variable already exists at that time of the execution of the line, and
so cannot be created at that time.
The existence of the line - var x = 5; - does result in "The creation of
a local variable of the 'innerFunction' function named 'x'" and may
result in "the assignment of the value 5 to that variable" but the
_execution_ of the line does not. #9 is not possible.
10. The assignment of the value 5 to a declared local variable
of the 'innerFunction' function named 'x'.
x = 5; This is entirely possible. #10 is possible (getting this one
wrong is fatal).
var x = 5; - This is entirely possible, and you don't need any other
variable declarations for 'x' (even though they are allowed (not
encouraged)) because, as I mentioned for #9, the variable declaration
here has created a local variable during variable instantiation, and so
at the time of executing the line a local variable of the
'innerFunction' is certain to be in existence. #10 is possible (getting
this one wrong is fatal).
11. The creation of a global variable named 'x' and the
assignment of the value 5 to that variable.
x = 5; Here is the hair-splitting quibble; The declaration of a
variable results in the creation of a property of an object (a Variable
object) and the declaration of a global variable results in the creation
of a property of the global object (because the global object is used as
the Variable object for variable instantiation in the global execution
context). And if no object on the scope chain is found to have an 'x'
property then the result of the assignment is the creation of an 'x'
property of the global object, which certainly is also a consequence of
declaring a global variable.
However, there are two reasons for not considering the runtime creation
of a property of the global object as being "the creation of a global
variable". The first, and most arguable, is timing; variables are
created during variable instantiation and prior to the execution of any
code for the execution context in question. The second is that
properties of objects created during variable instantiation in function
and global execution contexts are marked with the - DontDelete -
attribute. Thus a global variable cannot be deleted with the - delete -
operator, a runtime-created property of the global object can be deleted
so it is not a global variable. #11 is not possible. (Quibbling about
variable instantiation in - eval - code execution contexts remains a
possibility. And a candidate taking that line would act to impress
(possibly just for their knowing the specifics of the eval case)).
var x = 5; - Not possible, but remember that it is possible to put the
global object at the top of the scope chain, and if you can get at the
prototype for the global object (if it has one at all) then the
technique used in #1 can be applied to create a property of the global
object with the execution of this line of code. So it is a good thing
that creating a property of the global object alone could not be
considered as "the creation of a global variable". #11 is not possible.
12. The assignment of the value 5 to a declared global
variable named 'x'.
x = 5; - #12 is possible (getting this wrong is fatal).
var x = 5; If the global object is the one placed at the top of the
scope chain by the - with - statement, and it has a global variable
named 'x' then the execution of this line of code will assign the value
5 to that global variable. #12 is possible.
13. The creation of an 'x' property of the global object and the
assignment of the value 5 to that property.
x = 5; - #13 is possible (getting this wrong is fatal).
var x = 5; ECMA 262 3rd Ed. is unspecific on the subject of the
prototype of the global object. It allows that the global object may
have no prototype at all, but leaves it to the implementation to decide
how to handle the situation. Now suppose the global object inherits
from - Object.prototype -, which is hardly an unexpected thing for an
object in javascript to do, then the technique used in #1 can be applied
here to create a property of the global object at runtime. #13 is
possible.
14. The assignment of the value 5 to a pre-existing 'x' property
of the global object.
x = 5; - #14 is possible (getting this wrong is fatal).
var x = 5; If the global object is the one placed at the top of the
scope chain by the - with - statement, and it has an 'x' property then
the execution of this line of code will assign the value 5 to that 'x'
property of the global object. #14 is possible.
15. The creation of an 'x' property of the window object and
the assignment of the value 5 to that property.
x = 5; - There is no requirement that there be a window object at all,
and in WSH and ASP, for example, there is no window object (the concept
has no meaning in host contexts), but in a web browser environment the
window object is expected to be the same object as the global object
(and expectation that can be observed as being rewarded in actual
browsers). There is nothing in the questions that preclude the code from
being executed in a web browser where the ECMAScript global object is
the window object, and so #15 is possible.
var x = 5; - if the window object and the global object are the same
object then whatever can be done with the global object can also be done
with the window object, so as #13 is possible #15 is possible.
16. The assignment of the value 5 to a pre-existing 'x' property
of the window object.
x = 5; - If the window object and the global object are the same object
then whatever can be done with the global object can also be done with
the window object, so as #14 is possible #16 is possible.
var x = 5; - If the window object and the global object are the same
object then whatever can be done with the global object can also be done
with the window object, so as #14 is possible #16 is possible.
<snip>
x = 5; - Disregarding possible sources of errors on the preceding line
(or in other places in the code) which may prevent the subject line of
code being executed (and so are precluded by the presupposition that it
is executed implied by the question), it has been demonstrated that this
line can be made to error in environments as common as IE 6. #17 is
possible.
var x = 5; - If, in IE 6, you create the conditions that have been
described to make - x = 5; - error, and then place the window/global
object at the top of the scope chain with the - with - statement, then
you get the same error with - var x = 5; -. #17 is possible.
------------------------------------------------------
Given the number of seriously counter-intuitive possibilities introduced
by the use of the - with -, particularly things like - var x = 5; -
occurring nested two functions deep can still set the value of a global
variable, I hope that leaves nobody in any doubt as to why the use of
the - with - statement is _strongly_ discouraged in production
javascript. But still, if you have applied for a job as a javascript
expert is there an excuse for not knowing what the - with - statement
does, or not understand code that does use it?
It should also be seen that the strict observance of many other 'best
practice' notions would impact on these questions. Such as 'if you are
using something that is conceptually a global variable it should always
be explicitly declared as a global variable'. Which precludes the stated
IE 6 runtime errors, any questions of playing around with the prototype
of the global object and any hair splitting about whether a property of
the global object is a variable or not. And the suggestion that
function local variables should all be explicitly declared at the start
of a function body (so in a context that mirrors the order of their
handling, i.e. declarations being handled during variable instantiation,
prior to the actual execution of any function body code) guarantees that
they cannot then appear inside a Block Statement, and so cannot appear
where they may be (or appear to be) influenced by a scope chain
augmented by the - with - statement.
Then again, there were also counter-intuitive possibilities introduced
by prototype inheritance, which is pretty fundamental to what javascript
is.
Going back to my inspiration for the question, the meebo.com page's
question:-
| 2. What's the difference between these two statements:
|
| a. var x = 3;
|
| b. x = 3;
- (Remembering that the only answer to the questions above that changed
as a result of using - var - was the answer to #8.) does anyone still
think it can be answered at all? Or answered concisely but not
trivially? Or that the person setting that question would see any
accurate answer given as an indication of the expertise they were
looking for?
Richard.