One of the strict mode rules (Annex C) states:
When a delete operator occurs within strict mode code, a SyntaxError is thrown if its UnaryExpression is a direct reference to a variable, function argument, or function name.
So in this code:
delete x
x is a reference. (I know this because "the result of evaluating an Identifier is always a value of type Reference"). But is it a direct reference?
And, are there other kinds of references? Indirect references? (If not, what's the point of using the word "direct" at all?)
Yes, there are different kinds of References (EcmaScript §8.7). The member operators (EcmaScript §11.2.1) for example do result in references whose base value is the value of the baseReference, which I'd call "not direct". A "direct reference" would be an identifier reference (EcmaScript §10.2.2.1, where the base value is an Environment Record.
Anything that's not defined as a property if I understand it correctly.
These should throw errors or fail in a console:
(function(){ 'use strict'; var x = '2'; delete x; })();
(function(){ 'use strict'; delete arguments[0]; })('2');
Related
If I open a javascript console on a web page and add a variable:
var abc = "Hello";
Javascript attaches that property to the window object so I can simply access the object by window.abc or directly abc
console.log(window.abc)
console.log(abc)
Now, if I try to access something not defined yet through the window object
console.log(window.notdefinedyet)
> undefined
It simply returns undefined. So if I do
console.log(notdefinedyet)
why is that an error and not just undefined?
Because trying to read the value of an undeclared identifier is a ReferenceError. This is fundamentally a different operation from trying to read a property from an object that the object doesn't have.
Trying to use an undeclared identifier is usually a bug (for instance, a typo). That's part of why ES5's strict mode made assigning to an undeclared identifier a ReferenceError (instead of the old behavior — still in loose mode for backward-compatibility — where it creates a new global variable, something I call The Horror of Implicit Globals).
One could argue (and many have) that trying to get a property from an object that the object doesn't have is also a bug. But it's often really useful to get undefined instead of an error in that case. Whether one agrees with that distiction, this is how JavaScript is defined.
It's probably worth noting tangentially that this business of global variable declarations creating properties on the global object is legacy behavior that has to remain in place for backward compatibility, but isn't consistent with current thought on language design in TC39 (the committee behind JavaScript standards). Newer constructs like let, const, and class don't create properties on the global object even when used at global scope (although they do create globals):
var a = 1;
let b = 2;
console.log("a" in window); // true
console.log("b" in window); // false
console.log(a); // 1
console.log(b); // 2
It always check for reference so console.log(notdefinedyet) gives an error but in case of console.log(window.notdefinedyet), window has reference of object type built in only thing it does not have notdefinedyet key
Since ECMAScript 6, most function objects have a name property defined.
Now, if an anonymous function expression is assigned to a variable or is part of a property definition inside an object initializer, the identifier of the variable or the name of the property will be the value of the name property of the function object.
const a = function () {};
console.log(a.name); // a
const object = {
b : function () {}
};
console.log(object.b.name); // b
I don't have problems to understand the spec regarding the documented semantic of this behaviour, but I can't really see, why an assignment to a left hand side member expression as in the following example does not affect the name property of the function, which seems to be the case.
const object = {};
object.c = function () {};
console.log(object.c.name); //
As part of the member expression, there is obviously an indentifier which could (and should?) be used as the value of the name property. Even if the property name would be an expression inside brackets, this should be no problem, since using computed property names inside an object initializer does not prevent the name property of the anonymous function to be defined either.
It would be great, if someone could confirm that the observed behaviour is conforming to the spec and anyway, explain in short the specific semantics that apply to this syntax.
This first snippet is described under assignment operators:
e. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then
i. Let hasNameProperty be HasOwnProperty(rval, "name").
ii. ReturnIfAbrupt(hasNameProperty).
iii. If hasNameProperty is false, perform SetFunctionName(rval, GetReferencedName(lref)).
When you assign to a MemberExpression, as in your last snippet, IsIdentifierRef(LeftHandSideExpression) is false and no conversion takes place.
If you search the standard for IsAnonymousFunctionDefinition you'll find a couple of other cases where this logic is used (object initializers, destructuring assignments).
IsIdentifierRef is defined twice (here and here), and both definitions boil down to "if an expression is IdentifierReference return true otherwise return false", where IdentifierReference is an Identifier (the yield stuff is for backward compatibility with non-strict code).
I start to read JavaScript Patterns, some codes confused me.
var global = (function () {
return this || (1, eval)('this');
}());
Here are my questions:
Q1:
(1, eval) === eval?
Why and how does it work?
Q2: Why not just
var global = (function () {
return this || eval('this');
}());
or
var global = (function () {
return this;
}());
The difference between (1,eval) and plain old eval is that the former is a value and the latter is an lvalue. It would be more obvious if it were some other identifier:
var x;
x = 1;
(1, x) = 1; // syntax error, of course!
That is (1,eval) is an expression that yields eval (just as say, (true && eval) or (0 ? 0 : eval) would), but it's not a reference to eval.
Why do you care?
Well, the Ecma spec considers a reference to eval to be a "direct eval call", but an expression that merely yields eval to be an indirect one -- and indirect eval calls are guaranteed to execute in global scope.
Things I still don't know:
Under what circumstance does a direct eval call not execute in global scope?
Under what circumstance can the this of a function at global scope not yield the global object?
Some more information can be gleaned here.
EDIT
Apparently, the answer to my first question is, "almost always". A direct eval executes from the current scope. Consider the following code:
var x = 'outer';
(function() {
var x = 'inner';
eval('console.log("direct call: " + x)');
(1,eval)('console.log("indirect call: " + x)');
})();
Not surprisingly (heh-heh), this prints out:
direct call: inner
indirect call: outer
EDIT
After more experimentation, I'm going to provisionally say that this cannot be set to null or undefined. It can be set to other falsy values (0, '', NaN, false), but only very deliberately.
I'm going to say your source is suffering from a mild and reversible cranio-rectal inversion and might want to consider spending a week programming in Haskell.
The fragment
var global = (function () {
return this || (1, eval)('this');
}());
will correctly evaluate to the global object even in strict mode. In non-strict mode the value of this is the global object but in strict mode it is undefined. The expression (1, eval)('this') will always be the global object.
The reason for this involves the rules around indirect versus direct eval. Direct calls to eval has the scope of the caller and the string this would evaluate to the value of this in the closure. Indirect evals evaluate in global scope as if they were executed inside a function in the global scope.
Since that function is not itself a strict-mode function the global object is passed in as this and then the expression 'this' evaluates to the global object. The expression (1, eval) is just a fancy way to force the eval to be indirect and return the global object.
A1: (1, eval)('this') is not the same as eval('this') because of the special rules around indirect versus direct calls to eval.
A2: The original works in strict mode, the modified versions do not.
To Q1:
I think this is a good example of comma operator in JS. I like the explanation for comma operator in this article: http://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/
The comma operator evaluates both of its operands (from left to right) and returns the value of the second operand.
To Q2:
(1, eval)('this') is considered as indirect eval call, which in ES5 does execute code globally. So the result will be the global the context.
See http://perfectionkills.com/global-eval-what-are-the-options/#evaling_in_global_scope
Q1: Multiple consecutive javascript statements separated by a comma take the value of the last statement. So:
(1, eval) takes the value of the last one which is a function reference to the eval() function. It apparently does it this way to make the eval() call into an indirect eval call that will be evaluated in the global scope in ES5. Details explained here.
Q2: There must be some environment that doesn't define a global this, but does define eval('this'). That's the only reason I can think of for that.
I understand about how eval() works in non-strict contexts, however the case of using eval() in strict mode has completely befuddled me. When eval() is called directly in the global scope, variables are kept inside the new eval() scope:
'use strict';
eval('var a = 1;');
console.log(a); // ReferenceError: a is not defined
However, if I perform an indirect call to eval() in the global scope (should be the same thing, right?), it acts as though it is not in strict mode (if you don't believe me, see this JSFiddle):
'use strict';
(0, eval)('var a = 1;'); // indirect call to eval
console.log(a); // 1???
For an explanation of what (0, eval) does: see Why does google main page use (0, obj.func)(args) syntax?.
At least according to my understanding of how eval() is supposed to work in strict mode, it is meant to (no matter whether eval() is called directly or indirectly) create a new scope for variables defined in the eval() call, however this doesn't seem to be the case here. (ECMA-262 spec 5th ed 10.4.2)
This is the case in all major browsers (including Internet Explorer 10, Chrome 30 and Firefox 24) so I don't think it's a bug. Aren't they both meant to do the same thing, and if not, why is this the case?
Note: yes, I know the "dangers" of using eval() - I simply want to understand the logic behind this :)
tl;dr
The second (0, eval)('var a = 1;'); case is in fact not a direct call.
You can see this more prevalently in:
(function(){ "use strict"
var x = eval;
x("var y = 10"); // look at me all indirect
window.y;// 10
eval("var y = 11");
window.y;// still 10, direct call in strict mode gets a new context
})();
The issue can be seen in:
If the eval code is strict code, then (me: fix context)
But strict eval code is defined as:
Eval code is strict eval code if it begins with a Directive Prologue that contains a Use Strict Directive or if the call to eval is a direct call.
Since the call is not direct, the eval code is not strict eval code - and the execution is on global scope.
First of all great question.
"Eval Code" is more general than direct or indirect call to eval.
Let's check the exact specification for the eval function
15.1.2.1 eval (x)
When the eval function is called with one argument x, the following steps are taken:
If Type(x) is not String, return x.
Let prog be the ECMAScript code that is the result of parsing x as a Program. If the parse fails, throw a SyntaxError exception (but see also clause 16).
Let evalCtx be the result of establishing a new execution context (10.4.2) for the eval code prog.
Let result be the result of evaluating the program prog.
Exit the running execution context evalCtx, restoring the previous execution context.
...
So, let's explore what 10.4.2 tells us, you cited that - in specific let's look at the first clause:
If there is no calling context or if the eval code is not being evaluated by a direct call (15.1.2.1.1) to the eval function then ... Initialise the execution context as if it was a global execution context
So what is a direct call?
A direct call to the eval function is one that is expressed as a CallExpression that meets the following two conditions:
The Reference that is the result of evaluating the MemberExpression in the CallExpression has an environment record as its base value and its reference name is "eval".
The result of calling the abstract operation GetValue with that Reference as the argument is the standard built-in function defined in 15.1.2.1.
So, what's the MemberExpression in both cases?
In eval('var a = 1;'); indeed the result of evaluating it has a reference name eval and calling GetValue resolution on it returns the built in function.
In (0, eval)('var a = 1;'); the result of evaluating the member expression does not have a reference name eval. (It does resolve to the built in function on GetValue though).
What are reference names anyway?
Section 8.7 in the spec tells us:
A Reference is a resolved name binding. A Reference consists of three components, the base value, the referenced name and the Boolean valued strict reference flag. The base value is either undefined, an Object, a Boolean, a String, a Number, or an environment record (10.2.1). A base value of undefined indicates that the reference could not be resolved to a binding. The referenced name is a String.
This requires us to look into the GetReferencedName:
GetReferencedName(V). Returns the referenced name component of the reference V.
So, while the expression (0,eval) === eval is true, when evaluating the function, this is actually an indirect call because of naming.
May I offer the Function constructor instead :)?
Everything I've ever read indicates that in Javascript, the boolean value of an undefined variable is False. I've used code like this hundreds of times:
if (!elem) {
...
}
with the intent that if "elem" is undefined, the code in the block will execute. It usually works, but on occasion the browser will throw an error complaining about the undefined reference. This seems so basic, but I can't find the answer.
Is it that there's a difference between a variable that has not been defined and one that has been defined but which has a value of undefined? That seems completely unintuitive.
What is a ReferenceError?
As defined by ECMAScript 5, a ReferenceError indicates that an invalid reference has been detected. That doesn't say much by itself, so let's dig a little deeper.
Leaving aside strict mode, a ReferenceError occurs when the scripting engine is instructed to get the value of a reference that it cannot resolve the base value for:
A Reference is a resolved name binding. A Reference consists of three
components, the base value, the referenced name and the Boolean valued
strict reference flag. The base value is either undefined, an Object,
a Boolean, a String, a Number, or an environment record (10.2.1). A
base value of undefined indicates that the reference could not be
resolved to a binding. The referenced name is a String.
When we are referencing a property, the base value is the object whose property we are referencing. When we are referencing a variable, the base value is unique for each execution context and it's called an environment record. When we reference something that is neither a property of the base object value nor a variable of the base environment record value, a ReferenceError occurs.
Consider what happens when you type foo in the console when no such variable exists: you get a ReferenceError because the base value is not resolvable. However, if you do var foo; foo.bar then you get a TypeError instead of a ReferenceError -- a subtle perhaps but very significant difference. This is because the base value was successfully resolved; however, it was of type undefined, and undefined does not have a property bar.
Guarding against ReferenceError
From the above it follows that to catch a ReferenceError before it occurs you have to make sure that the base value is resolvable. So if you want to check if foo is resolvable, do
if(this.foo) //...
In the global context, this equals the window object so doing if (window.foo) is equivalent. In other execution contexts it does not make as much sense to use such a check because by definition it's an execution context your own code has created -- so you should be aware of which variables exist and which do not.
Checking for undefined works for variables that have no value associated but if the variable itself hasn't been declared you can run into these reference issues.
if (typeof elem === "undefined")
This is a far better check as doesn't run the risk of the reference issue as typeof isn't a method but a keyword within JavaScript.
Is it that there's a difference between a variable that has not been defined and one that has been defined but which has a value of undefined?
Yes. A undeclared variable will throw a ReferenceError when used in an expression, which is what you're seeing.
if (x) { // error, x is undeclared
}
Compared to;
var y; // alert(y === undefined); // true
if (y) { // false, but no error
}
That seems completely unintuitive.
Meh... what I find unintuitive:
if (y) // error, y is undeclared
var x = {};
if (x.someUndeclaredAttribute) // no error... someUndeclaredAttribute is implictly undefined.
Here's an example of Jon's explanation regarding environment records:
var bar = function bar() {
if (!b) { // will throw a reference error.
}
},
foo = function foo() {
var a = false;
if (a) {
var b = true;
}
if (!b) { // will not throw a reference error.
}
};
In strict mode, it is an error:
function a() {
"use strict";
if (!banana) alert("no banana"); // throws error
}
It's always really been better to make an explicit test for globals:
if (!window['banana']) alert("no banana");
It doesn't make sense to perform such a test for non-global variables. (That is, testing to see whether the variable is defined that way; it's fine to test to see whether a defined variable has a truthy value.)
edit I'll soften that to say that it rarely makes sense to thusly test for the existence of non-globals.
When a variable is declared and not initialized or it's used with declaration, its value is "undefined". The browser complains exactly when this undefined variable is referenced. Here "reference" means some piece of javascript code is trying to visit an attribute or method of it. For example, if "elem" is undefined, this will throw an exception:
elem.id = "BadElem";
or you use try/catch:
try { x } catch(err){}
This way you're not doing anything in case of an error but at least your script won't jump off the cliff...
undefined = variable exists but has no value in it
ReferenceError = variable does not exist