JavaScript/Notes/Scope: Difference between revisions

From Noisebridge
Jump to navigation Jump to search
Line 105: Line 105:
==Omitting var in VariableDeclaration==
==Omitting var in VariableDeclaration==
Assignment without var is not a variable declaration.
Assignment without var is not a variable declaration.
 
<pre>
| `foo = 1;`
| `foo = 1;`
</pre>


The identifier foo must be resolved up the scope chain (see 11.13.1  
The identifier foo must be resolved up the scope chain (see 11.13.1  
Simple Assignment, also below). If foo is not found, a foo property is  
Simple Assignment, also below). If foo is not found, a foo property is  
created on the global object (see 8.7.2 PutValue).
created on the global object (see 8.7.2 `PutValue`).


<pre>
| 11.13.1 Simple Assignment (= )
| 11.13.1 Simple Assignment (= )
|  The production AssignmentExpression : LeftHandSideExpression =
|  The production AssignmentExpression : LeftHandSideExpression =
Line 120: Line 122:
|  4.Call PutValue(Result(1), Result(3)).
|  4.Call PutValue(Result(1), Result(3)).
|  5.Return Result(3).
|  5.Return Result(3).
</pre>


Step 4 leads to PutValue(foo, 1);
Step 4 leads to `PutValue(foo, 1)`


<pre>
| 8.7.2 PutValue(V, W)
| 8.7.2 PutValue(V, W)
|  1. If Type(V) is not Reference, throw a ReferenceError exception.
|  1. If Type(V) is not Reference, throw a ReferenceError exception.
Line 133: Line 137:
| GetPropertyName(V) for the property name and W for the value.
| GetPropertyName(V) for the property name and W for the value.
|  7. Return.
|  7. Return.
</pre>


Step 2, GetBase(v) is null, so that leads to step 6. That leads to  
Step 2, `GetBase(v)` is `null`, so that leads to step 6. That leads to  
[[Put]](foo, 1) on the global object.
`[[Put]]` (foo, 1) on the global object.


<pre>
| 8.6.2.2 [[Put]](P, V)
| 8.6.2.2 [[Put]](P, V)
|  When the [[Put]] method of O is called with property P and value V,
|  When the [[Put]] method of O is called with property P and value V,
Line 151: Line 157:
|  Note, however, that if O is an Array object, it has a more elaborate
|  Note, however, that if O is an Array object, it has a more elaborate
| [[Put]] method (15.4.5.1).
| [[Put]] method (15.4.5.1).
 
</pre>
Note step 6: Create a property with name P, set its value to V and give  
Note step 6: Create a property with name P, set its value to V and give  
it empty attributes.
it empty attributes.


Global object properties (user-defined ones, at least) are [[Deletable]],
Global object properties (user-defined ones, at least) are [[Configurable]],
variables, global or otherwise, are not.
variables, global or otherwise, are not.
User-defined properties are, by default, [[Configurable]], which means that they can be deleted.
| If code is eval code, then let configurableBindings be true else let configurableBindings be false.
http://www.ecma-international.org/ecma-262/5.1/#sec-10.5

Revision as of 20:45, 26 October 2013

Scope Chain and Identifier Resolution.

From global object property access.

I am currently seeing some weird behavior with the global object: <source lang="javascript"> console.log(window.foo); // this returns undefined console.log(this.foo); // this returns undefined console.log(foo); // this is a reference error </source>

The Mozilla docs on Reference error simply state:

A ReferenceError is thrown when trying to dereference a variable that has not been declared.

I realize accessing a non existent property on an object should return undefined... which accounts for the first two cases... but whats going on in the third case?

As expected, getting a property off an object, the prototype chain is searched. When that happens, if the property is not found, then `undefined` results.

But with scope chain resolution, when the property is not resolved, an error results.

| 11.1.2 Identifier Reference | An Identifier is evaluated by performing Identifier Resolution | as specified in 10.3.1. The result of evaluating an Identifier | is always a value of type Reference.

...

| 10.3.1 Identifier Resolution | Identifier resolution is the process of determining the binding of an | Identifier using the `LexicalEnvironment` of the running execution context.

> console.log(foo); // this is a reference error >

Identifier `foo` is resolved to a Reference with null as the base object. In ES5, it looks as if it is a Reference with base object as `undefined`. With either spec, the result will be the same: `ReferenceError`. ES5 gets a little fancy with the explanation.

> The Mozilla docs on Reference error simply state: > > A ReferenceError is thrown when trying to dereference a variable that > has not been declared. >

They mean that when you try and get the value of an Identifier in a PrimaryExpression and the Identifier is not resolved, then the base object is null (or now `undefined`) that the attempt to get at the value is going to result in a ReferenceError.

So when you have an Expression like:

console.log(foo);

or even just a PrimaryExpression:

foo // a PrimaryExpression.

Identifier `foo` must be first resolved. The base object for that value is null (or so"undefined") and the when the expression is evaluated, it tries to get the value, and then finds the base object is null and throws a ReferenceError is thrown.

| 8.7.1 GetValue (V) | | 1. If Type(V) is not Reference, return V. | 2. Let base be the result of calling GetBase(V). | 3. If IsUnresolvableReference(V), throw a ReferenceError exception.

...

| IsUnresolvableReference(V). Returns true if the base value | is undefined and false otherwise.

The MDC docs might not say it, and you didn't ask, either, but in strict code, assignment to undeclared Identifier will result in referenceerror too.

> I realize accessing a non existent property on an object should return > undefined... which accounts for the first two cases... but whats going > on in the third case?

An Identifier resolution was performed on the scope chain. Just remember the difference when getting a property fails: With object properties - the prototype chain is used and the result is undefined. With unqualified Identifiers, the scope chain is searched in the result is ReferenceError.


Omitting var in VariableDeclaration

Assignment without var is not a variable declaration.

| `foo = 1;`

The identifier foo must be resolved up the scope chain (see 11.13.1 Simple Assignment, also below). If foo is not found, a foo property is created on the global object (see 8.7.2 `PutValue`).

| 11.13.1 Simple Assignment (= )
|   The production AssignmentExpression : LeftHandSideExpression =
| AssignmentExpression is evaluated as follows:
|   1. Evaluate LeftHandSideExpression.
|   2. Evaluate AssignmentExpression.
|   3.Call GetValue(Result(2)).
|   4.Call PutValue(Result(1), Result(3)).
|   5.Return Result(3).

Step 4 leads to `PutValue(foo, 1)`

| 8.7.2 PutValue(V, W)
|   1. If Type(V) is not Reference, throw a ReferenceError exception.
|   2. Call GetBase(V).
|   3. If Result(2) is null, go to step 6.
|   4. Call the [[Put]] method of Result(2), passing GetPropertyName(V)
| for the property name and W for the value.
|   5. Return.
|   6. Call the [[Put]] method for the global object, passing
| GetPropertyName(V) for the property name and W for the value.
|   7. Return.

Step 2, `GetBase(v)` is `null`, so that leads to step 6. That leads to `Put` (foo, 1) on the global object.

| 8.6.2.2 [[Put]](P, V)
|   When the [[Put]] method of O is called with property P and value V,
| the following steps are taken:
|   1. Call the [[CanPut]] method of O with name P.
|   2. If Result(1) is false, return.
|   3. If O doesn't have a property with name P, go to step 6.
|   4. Set the value of the property to V. The attributes of the
| property are not changed.
|   5. Return.
|   6. Create a property with name P, set its value to V and give it
| empty attributes.
|   7. Return.
|   Note, however, that if O is an Array object, it has a more elaborate
| [[Put]] method (15.4.5.1).

Note step 6: Create a property with name P, set its value to V and give it empty attributes.

Global object properties (user-defined ones, at least) are Configurable, variables, global or otherwise, are not.

User-defined properties are, by default, Configurable, which means that they can be deleted.

| If code is eval code, then let configurableBindings be true else let configurableBindings be false.

http://www.ecma-international.org/ecma-262/5.1/#sec-10.5