So I was reading this book, following along with the code examples and running them using node console. In Chapter 7, paragraph 'Lexical Versus Dynamic Scoping', the author claims that the following code will result in an error:
const x = 3;
function f() {
console.log(x); // this will work
console.log(y); // this will cause a crash
}
const y = 3;
f();
This is due to the fact that (as the book reads)
Scoping in JavaScript is lexical...
and
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 this code runs just fine, and produces the following output:
3
3
I have searched for other examples of lexical scoping and what it means, but they all seem to suggest slightly different things from what the book says. So I am left wondering, is the explanation in the book completely wrong or am I missing something very basic?
The quoted description…
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.
… is correct.
The code example is wrong. The function f has access to any variable (or constant) that exists in the scope that it is declared in. What the book appears to get wrong is that constants can be added to that scope after the function has been declared.
Where the function is declared matters. When it is declared, not so much.
The above code will work fine. However, this will fail.
const x = 3;
function f() {
console.log(x); // this will work
console.log(y); // this will cause a crash
}
f();
const y = 3;
That is because y is not yet declared or defined at the time of function invocation and
x is accessible because Scoping is lexical and function will access the x defined in global scope, if not found on local.
This can be understood by this example more clearly
function b(){
console.log(v) //=> 1
} //lexically defined at global scope
function a(){
var v = 2
console.log(v) //=> 2
b() //called in the scope of a()
}
var v = 1
a()
console.log(v) //=> 1
console.log in b() will give 1 because according to lexical global scope v = 1. But according to Dynamic Scope (created by a()) it should be 2, which is not the case.
Hope this helps :)
This book looks quite old - 11 years ago. The world is no longer the same. Now we see that JavaScript changes day after day. I think that we should not use any book produced since 2 years ago.
The problem you (and the book) mentioned is relating to another concept: strict mode. In ECMAScript 5, you can turn on this mode by adding "use strict" on top of your JavaScript file. So that, you would see some errors throw while they don't in regular mode. If you want to get the example works, you may need to find an old browser, IE8 or such - I'm not sure, then create a HTML file, then add your script to, then turn on strict mode, it may give you the same result as the author said.
Strict mode is no longer the case in JavaScript today - I would like to talk about ECMAScript 6, or ES6, or ECMAScript 2015. We simple don't need to care about strict mode while writing JavaScript now. I have many module on npmjs.org but none of them have "use strict" declaration at all.
Just my two cents: in programming, don't read old books, because everything changes so fast.
Related
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"
In a JavaScript debugger, I can manually inspect the scope chain of a function. For instance, when executing foo() on this piece of code:
var x1 = "global";
var foo = (function main () {
var x2 = "inside obj";
return function internalFoo () {
var x3 = "inside internalFoo";
console.log (x1+','+x2+','+x3); // get the scopes
};
})();
foo ();
and setting a breakpoint on the console.log, I see the following scopes:
Is there some means to do this programmatically?
How can I inspect what is defined at every scope level?
I am (pretty) sure this is not possible.Not even the Chrome-debugger keeps track of your scope all the time, but only when it hits a breakpoint. Keeping track of the scope chain for all the time would cost way too much memory (depending on the complexity of your closures and contexts). See this feature request for further information: https://groups.google.com/forum/#!topic/google-chrome-developer-tools/wKEMpKjXR7s
ECMA 262 (10.3.1) describes how Identifier Resolution has to be done, the most important part of this is to call GetIdentifierReference (lex, name, strict) which is described in ECMA 262 (10.2.1). As far as I know there is no command in any implementation of ECMAScript to call this function at runtime.
However this question (or to be precise it's answer) might be interesting as it comes at least closer to what you asked for.
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.
Having coded JavaScript since 1996, I have a very simple issue which I could not clearly prove/disprove using jsfiddle
In some JS attached to a CV I spotted some issues that I would like to verify - one of which is
multiple declaration of variable in the same function
Testing it seems it is allowed in newer browsers (OSX Chrome16 Fx 10beta) - as far as I remember it used to give errors (Netscape/Mozilla/Fx1/IE5 or so) :
function something() {
var var1 = "";
.
/* reams of code which scrolls the first declaration off the screen
so the author likely forgot the var was already declared earlier
in the same function */
.
var var1 = ""; // could this result in an error in some browsers?
}
My fiddle is here
Not as far as I'm aware, I've seen javascript functions with multiple for loops each declaring their own var i for at least 6 years all without issue.
Having looked at the spec, there seems to be nothing concrete in this area, however since (particularly global) variable name overwrites (read: clashes) has been a feature since inception I would be very surprised if a restraint upon multiple declarations was imposed.
As it stands, I would suggest that it doesn't show particularly good knowledge of scoping (and hoisting) in javascript, but is valid code nonetheless.
I've been playing with javascript for about as long as you, and I don't remember this ever being forbidden by an interpreter. Looking at the ECMAScript 1 spec, I can see no mention of ensuring that variable declarations are unique within a given scope; and it would be a strange feature to remove.
The absence of such a check, alongside the fact that variables are function~ and not block-scoped, is one of the things that seems to cause misunderstanding, where a variable "declared" in one block unexpectedly has the last value it had in a previous block.
I'm quite certain that the parser ignores the second "var"-declaration, since it is redundant - all it does is signify that the variable is limited to the local scope. There's no reason to use it twice in the same scope, as in the same function, but if you, as #rich.okelly pointed out, have loops or functions, you can indeed use "var" to create a local variable of the same name as a variable in a higher scope. It's not pretty, and certainly doesn't do any wonders for readability, but it's possible. I have never encountered a browser that hangs up on prepending a variable with "var" twice.
Example:
x = "Hello";
function test() {
alert(x); // Outputs "Hello"
}
function test2() {
var x = "local variable";
alert(x); // ouputs "local variable"
var x = "changed the variable"; // this does exactly the same thing as if you'd omitted "var"
}
alert(x); // Outputs "Hello"
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.'
}