Eloquent javscript writes this in chapter 10 of the modules chapter:
The most obvious way is the special operator eval, which will execute
a string of code in the current scope. This is usually a bad idea
because it breaks some of the sane properties that scopes normally
have, such as being isolated from the outside world.
Here is the code:
function evalAndReturnX(code) {
eval(code);
return x;
}
console.log(evalAndReturnX("var x = 2"));
console.log(x)
console.log(code)
it outputs:
2
ReferenceError: x is not defined (line 7)
which seems normal? What gives? I don't see any violation of scope?
Eval has some tricky semantics. From mdn:
If you use the eval function indirectly, by invoking it via a
reference other than eval, as of ECMAScript 5 it works at global scope
rather than local scope; this means, for instance, that function
declarations create global functions, and that the code being
evaluated doesn't have access to local variables within the scope
where it's being called.
So in your case, because you're calling eval directly within that function, it defines x within the lexical scope of that function, not available to the global scope. However, if you were to do something like this:
var _eval = eval;
function e() {
_eval("var x = 2");
return x;
}
console.log(e());
console.log(x);
It would work in both cases, since this would define x in the global scope, which would be inherited by the lexical scope of the callee.
See this fiddle for a working example
Related
Im confused about javascript having lexical scope.
Lexical scope: Lexical scoping means whatever variables are in scope where you define a function from (as opposed to when you call it) are in scope in the function.
However in JS its: scope isn't assigned when the function is defined and compiled rather it's assigned at runtime i.e. when the function is called
In the below example: If JS had lexical scope, I would get an error. However I do not because of hoisting and at run time js engine checks for x.
function f() {
console.log(x);
}
const x = 10;
f(); // 10
Can someone explain with an example how JS can have lexical scope? Thanks!
However in JS its: scope isn't assigned when the function is defined and compiled rather it's assigned at runtime i.e. when the function is called
I think you are confusing yourself with this description of scope in JS. A more accurate description of scope in JS is given by this article (emphasis added):
Because functions are also defined at this time (lexing phase), we can say that lexical scope is based on where variables and blocks of scope exist at author time, and thus are locked down at the end of the lexing phase. Scope is not defined at runtime, rather it can be accessed at runtime.
So, in your example:
function f() {
console.log(x);
}
const x = 10;
f(); // 10
What is happening is that the scope chain of function f consists of:
its local scope
the outer, global scope
and this "scope chain" is defined at author time (i.e. lexical scope).
At author time, the constant x is defined within the global scope, and when f is called, x is available within the global scope. That x is not defined when the function f is declared (but NOT called) is irrelevant for this discussion.
Also, you are incorrect when you mention "hoisting" regarding your code example: variables declared with const are NOT hoisted.
If we alter your example, we will get a Reference Error, since the constant x has not been initialized in the global scope (i.e. on author time), when the function f is called (and function f looks through its scope chain, which has been defined on author time):
function f() {
console.log(x);
}
f(); // 10
const x = 10;
See below example, everything outside function f is compiled and vars are hoisted.
But the contents inside function f are not hoisted/parsed until it's called. And the local variables inside f remain accessible within the function (i.e. the lexical scope of function).
function f() {
console.log(x);
var a = 20;
}
const x = 10;
f(); // 10
console.log(a);
I'm learning JS by reading a book (Learning JavaScript by Ethan Brown) where author writes:
Lexical scoping means whatever variables are in scope where you define
a function from (as opposed to when you call it) are in scope in the
function. Consider this example:
const x = 3;
function f() {
console.log(x); // this will work
console.log(y); // this will cause a crash
}
const y = 3;
f();
The variable x exists when we define the function f , but y doesn’t.
Then we declare y and call f , and see that x is in scope inside the
body of f when it’s called, but y isn’t. This is an example of lexical
scoping: the function f has access to the identifiers that
were available when it was defined, not when it was called
When I run it in VS Code debugger, there doesn't seem to be any errors.
I see
3
3
without any exceptions. Can this depend on ES version that I'm using or something else?
Though I think the author simply chose a poor (well, incorrect) piece of code to illustrate the point, the code is interesting as a way to illustrate something related but different. Because y is declared with const, it is visible inside the function because its declaration is hoisted to the nearest enclosing block (which is not explicitly visible in the posted code, but it must be there somewhere). However, if the function call is moved to before that declaration, you will see an error because the reference to y will happen in the somewhat weirdly-named "temporal dead zone". References to declared but uninitialized variables created with let or const are runtime errors.
If the declaration of y is changed to a var declaration, then moving the function call to before variable declaration does not cause an error because the variable is defined and in scope but is simply (old-school) uninitialized, and therefore simply undefined.
This works because const y=3 is declared before f() is called. If you move the declaration of y after f() then it will work as "expected"
As I understand it, Javascript doesn’t compile, it only runs. So there should be no compile-time errors, only runtime errors. So why doesn’t this code work?
function show() { console.log(x); }
(function () {
var x = 42;
show();
})()
My question isn’t on how to make this code better; I realize it is bad code and I already know how to fix it (see below).
My question is, why am I getting an Uncaught ReferenceError? If Javascript only throws errors at runtime, it should know that x == 42 at the time it calls show(), which is inside the anonymous function, correct?
working code:
(function () {
var x = 42;
function show() { console.log(x); }
show();
})()
working code, best option:
function show(y) { console.log(y); }
(function () {
var x = 42;
show(x);
})()
Note:Below description is as of ES5, NOT ES6 since in ES6 scoping rules have been changed(due to introduction of let).
Javascript does compile. It's just that like other languages such as c++/c# there is NO intermediate thing like exe/IL code that need to be clicked to start execution. In JS execution starts after compile phase.
So, when the compilation happens the compiler looks for function declaration and var declaration for variables. Therefore for this IIFE,
(function () {
var x = 42;
show();
})();
It does find one var declaration and the variable x is registered in the scope of the IIFE. This is called variable hoisting and x is available at the function level, in this case IIFE.
Later at execution time, the IIFE looks like this(conceptually):
(function () {
//registration of x in this function's scope has already happened at
//compile time. Notice absence of `var`
x = 42;
show();
})();
Now, at this time the engine talks to scope and asks for lvalue reference of x. Since x was registered in IIFE, engine gets one and then 42 is assigned to it.
Now for this part:
function show() { console.log(x); }
When show is called, engine first asks scope of show function for x, since it doesn't have any variable registered with the name x, global scope is asked( rvalue reference) for x, since it was never registered with global scope either during compilation phase, it cannot be found in any of the scope and we get reference error.
it should know that x == 42 at the time it calls show(), which is inside the anonymous function, correct?
Because of scoping rules, variables in outer scopes are visible in inner scope but not vice-versa. x inside IIFE is visible at the IIFE level and not outside in outer scope.
Variable hoisting in JS is fun.
It's saying giving you the Reference Error because you've defined x inside of a closure (a function defined inside another function), meaning it's not available in the global scope and the show() method doesn't know it exists. If you defined it first, globally, it would of course work.
That said, scoping has been significantly improved in ES6+ with the use of let and const, so unless you're stuck with vanilla JS you'll probably find those to give a much more consistent and predictable coding experience.
This is a useful read on the subject: How do JavaScript closures work?
show function gets scope where it's declared, not invoked.
People say that Eval brings dynamic scope into JavaScript, but I don't see how that statement is valid. Using Eval evaluates the expression using the same lexical environment/variable environment as the calling environment (reference ECMA 262 v. 5). The Assignment or declaration of the expression is obviously dynamic, but I don't think it is valid to say that it introduces dynamic scope.
Am I right in saying that Eval doesn't introduce dynamic scope?
Yes and no.
In a strict sense, no; the language still operates lexically (except with respect to this, which is always dynamically scoped).
However, if you read the whole question you linked to, you'll see that the asker is using eval to emulate dynamic scope.
var x = 1;
function g() {
print(x);
x = 2;
}
function f() {
// create a new local copy of `g` bound to the current scope
// explicitly assign it to a variable since functions can be unnamed
// place this code in the beginning of the function - manual hoisting
var g = eval(String(g));
var x = 3;
g();
}
f(); // prints 3
print(x); // prints 1
Emulating dynamic scope is totally achievable the way that the asker of that question is using it. The asker is using eval to actually import an externally defined function into the scope of another function. This requires stringifying the function and redeclaring it. So the externally defined function isn't really being run within the scope of another function (this example doesn't really demonstrate dynamic scope in a strict sense) because a whole new function is declared. That being said, the asker's intention is to emulate dynamic scope, and he is achieving that with eval.
Today I had a discussion with a colleague about nested functions in Javascript:
function a() {
function b() {
alert('boo')
}
var c = 'Bound to local call object.'
d = 'Bound to global object.'
}
In this example, trials point out that b is not reachable outside the body of a, much like c is. However, d is - after executing a(). Looking for the exact definition of this behaviour in the ECMAScript v.3 standard , I didn't find the exact wording I was looking for; what Sec.13 p.71 does not say, is which object the function object created by the function declaration statement is to be bound to. Am I missing something?
This is static scoping. Statements within a function are scoped within that function.
Javascript has a quirky behavior, however, which is that without the var keyword, you've implied a global variable. That's what you're seeing in your test. Your "d" variable is available because it is an implied global, despite being written within the body of a function.
Also, to answer the second part of your question: A function exists in whatever scope it is declared, just like a variable.
Sidenote:
You probably don't want global variables, especially not implied ones. It's recommended that you always use the var keyword, to prevent confusion and to keep everything clean.
Sidenote:
The ECMA Standard isn't probably the most helpful place to find answers about Javascript, although it certainly isn't a bad resource. Remember that javascript in your browser is just an implementation of that standard, so the standards document will be giving you the rules that were (mostly) followed by the implementors when the javascript engine was being built. It can't offer specific information about the implementations you care about, namely the major browsers. There are a couple of books in particular which will give you very direct information about how the javascript implementations in the major browsers behave. To illustrate the difference, I'll include excerpts below from both the ECMAScript specification, and a book on Javascript. I think you'll agree that the book gives a more direct answer.
Here's from the ECMAScript Language Specification:
10.2 Entering An Execution Context
Every function and constructor call
enters a new execution context, even
if a function is calling itself
recursively. Every return exits an
execution context. A thrown exception,
if not caught, may also exit one or
more execution contexts.
When control
enters an execution context, the scope
chain is created and initialised,
variable instantiation is performed,
and the this value is determined.
The
initialisation of the scope chain,
variable instantiation, and the
determination of the this value depend
on the type of code being entered.
Here's from O'Reilly's Javascript: The Definitive Guide (5th Edition):
8.8.1 Lexical Scoping
Functions in JavaScript are lexically
rather than dynamically scoped. This
means that they run in the scope in
which they are defined, not the scope
from which they are executed. When a
function is defined, the current scope
chain is saved and becomes part of
the internal state of the function.
...
Highly recommended for covering these kinds of questions is Douglas Crockford's book:
JavaScript, The Good Parts http://oreilly.com/catalog/covers/9780596517748_cat.gif
Javascript, The Good Parts, also from O'Reilly.
As I understand it, these are equivalent as far as scoping is concerned:
function a() { ... }
and
var a = function() { ... }
It seems important to note that while d is being created as a "global", it is in reality being created as a property of the window object. This means that you could inadvertently be overwriting something that already exists on the window object or your variable might actually fail to be created at all. So:
function a() {
d = 'Hello World';
}
alert(window.d); // shows 'Hello World'
But you cannot do:
function a() {
document = 'something';
}
because you cannot overwrite the window.document object.
For all practical purposes you can imaging that all of your code is running in a giant with(window) block.
Javascript has two scopes. Global, and functional. If you declare a variable inside a function using the "var" keyword, it will be local to that function, and any inner functions. If you declare a variable outside of a function, it has global scope.
Finally, if you omit the var keyword when first declaring a variable, javascript assumes you wanted a global variable, no matter where you declare it.
So, you're calling function a, and function a is declaring a global variable d.
...
function a() {
function b() {
alert('boo')
}
var c = 'Bound to local call object.'
d = 'Bound to global object.'
}
without being preceded by var, d is global. Do this to made d private:
function a() {
function b() {
alert('boo')
}
var c = 'Bound to local call object.'
var d = 'Bound to local object.'
}