My Problem Lies here I'm learning JavaScript But not new to Programming at all.
I understand hoisting, but with strict mode shouldn't this produce an error and be caught either when 6 is assigned to undeclared a variable or document.getElement... is assigned x this doesn't produce an error so my diagnosis is that hoisting is still going on..which i don't like and want to get rid of with using strict. Using Chrome Version 42.0.2311.152 m as my browser
function strictMode(){
'use strict';
try {
x = 6;
document.getElementById('hoisting').innerHTML = x;
var x;
}
catch(err) {
document.getElementById('error_report').innerHTML =
"There was an error that occured (Were in Strict Mode)" +
" " + err.message;
}
}
Variable declarations (i.e. var x;) are valid for the entire scope they are written in, even if you declare after you assign. This is what is meant by "hoisting": the var x; is hoisted to the beginning of the scope, and the assignment x = 6; is fine because x has been declared somewhere in that scope.
Strict mode does not change any of this. It would throw an error if you omitted the var x; declaration altogether; without strict mode, the variable's scope would implicitly be the global scope.
In ES2015 (a.k.a. ES6), hoisting is avoided by using the let keyword instead of var. (The other difference is that variables declared with let are local to the surrounding block, not the entire function.)
There are some weird things javascript allows that, as someone learning the language, you must learn to combat with good coding practices (simicolons are another good example). In the case of hoisting, it is generally good practice to declare your variables at the top of the scope where they would be hoisted to anyway. As already mentioned, strict mode is not a silver bullet and will not enforce this for you.
Related
I have the following code
"use strict";
x = 12;
function myFunction() {
}
myFunction();
so i am using use strict at the top of my script and now I am in strict mode - so I can't have undeclared variable like x
That gives me the following error in my console
Uncaught ReferenceError: x is not defined.
But when I execute this code
myFunction();
"use strict";
x = 12;
function myFunction() {
}
console.log('the script continues...')
I don't get the same error for my undeclared variable x. As you can see the script continues to execute.
Why is that ?
Why I don't get the same error when I try to call my hoisted function - so call the function before it is declared ?
Why it affects the global scope ?
As it says in MDN's documentation for strict mode, the "use strict"; sequence is only a strict mode directive if it's the first thing in the file other than a comment. With your function call above it, it's no longer the first thing in the file and so it isn't a strict mode directive; instead, it's just a freestanding string literal that nothing uses.
Since your code isn't in strict mode, x = 12; is assigning a value to an undeclared identifier, which (in loose mode) creates an implicit global. (More in my ancient post on my anemic old blog: The Horror of Implicit Globals.) This is one of the many reasons using strict mode is a Good Thing™, because with it assigning to an undeclared identifier is the error it always should have been.
Side note: Another way to use strict mode is to use native modules rather than the "use strict";. Modules, like nearly all new scopes in JavaScript since ES2015, are strict by default (and have other benefits, like their own scope and the ability to declare dependencies between them).
From MDN article about strict mode:
First, strict mode makes it impossible to accidentally create global
variables. In normal JavaScript mistyping a variable in an assignment
creates a new property on the global object and continues to "work"
(although future failure is possible: likely, in modern JavaScript).
Assignments, which would accidentally create global variables, instead
throw an error in strict mode:
'use strict';
// Assuming a global variable mistypedVariable exists
mistypeVariable = 17; // this line throws a ReferenceError due to the
// misspelling of variable
What does this mean? Does the engine detect if a variable with a similar name already exists(?!) or does strict mode merely prohibit declaring global variables outside of global scope?
The above quotation seems to hint the first possibility but this seems... weird?
The comment in the quoted code is misleading: The "Assuming" part is irrelevant. It should really read: "Assuming that no global variable with exactly this name was defined with let or var,..."
But do note what the purpose was of the code snippet and the comment: to show that in strict mode it is much easier to spot spelling mistakes. In sloppy mode the spelling mistake would go unnoticed (no errors) and you would be working with two variables without knowing it (at least for a while)
As requested, I'll make this into an answer :)
I think you understand what happens here already, and are just getting confused through reading that quote about "assuming a global variable..." too literally. (I will admit it is phrased in such a way as to possibly lead to this confusion though.) The real situation is very simple, and has nothing to do with variables having "similar names" (which JS has no concept of):
What is being talked about is what happens if you assign a value to a variable which has not been formally declared (variables are declared with one of the keywords var, let or const). Not declaring your variables is bad practice, and in strict mode will throw an error - this is a good thing and warns you of your mistake. But in non-strict mode, JS will happily accept this and think you wanted to declare a global variable of that name. This is almost never what you actually want, as it pollutes the global namespace, doesn't inform you of your error, and may lead to all sorts of tricky bugs later.
does strict mode merely prohibit declaring global variables outside of global scope?
strict mode definitely doesn't prohibit declaring global variables from anywhere. In non-strict mode, if you write:
someVar = 't';
it will evaluate to:
window.someVar = 't';
( why is this happening? ) despite of writing inside or outside of a function scope. actually, the line was both declaring and evaluation of variable ( look again, it doesn't have var so it shouldn't declare anything! ).
But it would cause such a side-effect that wasn't totally fine and they've introduced strict-mode which when is active, our first line will throw an error. because it's just evaluation without declaring it first.
now if you need to manipulate global scope inside of a function scope, you only should global object as reference:
var someGlobalVar;
var someOtherGlobalVar;
function hello() {
// this will make *someGlobalVar* to be redefined in the inner scope
// someGlobalVar != window.someGlobalVar
var someGlobalVar;
// manipulating inner variable, not global one
someGlobalVar = 's';
// it's fine, you've accessed global object correctly
window.someGlobalVar = 's';
// you're still editing global object, because
// there's no other variable inside of this scope having the same name
someOtherGlobalVar = 's';
}
I've been trying to figure out how the temporal dead zone/parsing of let and const work. This is what it seemingly boils down to (based on documentation and various responses I received in previous questions [such as this and this], though this goes against some answers given disagreement). Is this summary correct?
At the top of the scope, the JS engine creates a binding (an association of the variable keyword and name, e.g., let foo;) at the top of the relevant scope, which is considered hoisting the variable, but if you try to access the variable before the location of its declaration, JS throws a ReferenceError.
Once the JS engine moves down to the declaration (synonymous with "definition"), e.g., let foo;, the engine initializes it (allocating memory for it and making it accessible). The declaration is self-binding. (Here's the part that doesn't make sense to me: the binding is causing the hoisting at the top, but the engine doesn't initialize until it reaches the declaration, which also has a binding effect.) If there isn't an assignment, the value of the variable is set to undefined in the case of let or, if const is used, a SyntaxError will be thrown.
For reference here's what the specs say about it:
ECMAScript 2019 Language Specification draft: section 13.3.1, Let and Const Declarations
let and const declarations define variables that are scoped to the
running execution context's LexicalEnvironment. The variables are
created when their containing Lexical Environment is instantiated but
may not be accessed in any way until the variable's LexicalBinding is
evaluated. A variable defined by a LexicalBinding with an Initializer
is assigned the value of its Initializer's AssignmentExpression when
the LexicalBinding is evaluated, not when the variable is created. If
a LexicalBinding in a let declaration does not have an Initializer the
variable is assigned the value undefined when the LexicalBinding is
evaluated.
MDN Web Docs: Let
let bindings are created at the top of the (block) scope containing
the declaration, commonly referred to as "hoisting". Unlike variables
declared with var, which will start with the value undefined, let
variables are not initialized until their definition is evaluated.
Accessing the variable before the initialization results in a
ReferenceError. The variable is in a "temporal dead zone" from the
start of the block until the initialization is processed.
Maybe first you need to understand why the TDZ exists: because it prevents common surprising behaviour of variable hoisting and fixes a potential source of bugs. E.g.:
var foo = 'bar';
(function () {
console.log(foo);
var foo = 'baz';
})();
This is a frequent cause of surprise for many (novice) programmers. It's too late to change the behaviour of var now, so the ECMAScript group decided to at least fix the behaviour together with the introduction of let and const. How exactly it is implemented under the hood is a somewhat moot point, the important thing is that it stops what it most likely a typo/structural mistake dead in its tracks:
let foo = 'bar';
(function () {
console.log(foo);
let foo = 'baz';
})();
Practically speaking Javascript is executed in a two-step process:
Parsing of the code into an AST/executable byte code.
Runtime execution.
The parser will see var/let/const declarations in this first step and will set up scopes with reserved symbol names and such. That is hoisting. In the second step the code will act in that set up scope. It should be somewhat obvious that the parser/engine is free to do whatever it wants in that first step, and one of the things it does is to flag the TDZ internally, which will raise an error at runtime.
TDZ is quite complex to understand and requires a blog post to clarify how that actually works.
But in essence, overly simplified explanation is
let/const declarations do hoist, but they throw errors when accessed before being initialized (instead of returning undefined as var would)
Let's take this example
let x = 'outer scope';
(function() {
console.log(x);
let x = 'inner scope';
}());
the code above will throw a ReferenceError due to the TDZ semantics.
All these are from this great article completely about TDZ.
Kudos for the author.
Here is some sample code. I'd like to know if there is any reason why I shouldn't do this.
//some code
var x = "hello";
{
var y = "nice";
function myfunction() {
//do stuff . . .
}
}
The benefit of doing this I see is being able to organize sections of code in chunks and have auto formatters do some work with that...
In my tests {} does not affect the scope when creating a var or function.
This answer was written in times of earlier JavaScript implementations. While the same rules for var apply, ECMAScript 2015 (aka ES6) introduce the let variable declaration statement, which follows "traditional" block-scoped rules.
Example of let scoping with a Block, which logs "1", "2", "1":
{ let x = 1; console.log(x); { let x = 2; console.log(x) }; console.log(x) }
The MDN Reference on Block summarizes the usage of blocks as:
Important: JavaScript does not have block scope. Variables introduced with a block are scoped to the containing function or script, and the effects of setting them persist beyond the block itself. In other words, block statements do not introduce a scope. Although "standalone" blocks are valid syntax, you do not want to use standalone blocks in JavaScript, because they don't do what you think they do, if you think they do anything like such blocks in C or Java.
As discussed on MDN, the syntax is perfectly valid as { StatementList } (aka Block) is a valid Statement production..
However; and because this is very important: a new block does not introduce a new scope. Only function bodies introduce new scopes. In this case, both the x and y variables have the same scope.
In addition a FunctionDeclaration should appear only as a top-level statement - that is, it must be a statement directly under a Program or Function body, not a Block. In this case the declaration of myfunction is "not reliably portable among implementations".
There is the IIFE (Immediately Invoked Function Expression) pattern which can be used and, while it addresses the technical issues above, I would not recommend it here as it complicates the code. Instead, I would simply create more named functions (named functions can be nested!), perhaps in different modules or objects, as appropriate.
var x = "hello";
;(function () {
// New function, new scope
// "y" is created in scope, "x" is accessible through the scope chain
var y = "nice";
// Now VALID because FunctionDeclaration is top-level of Function
function myfunction() {
}
})(); // Invoke immediately
// No "y" here - not in scope anymore
Realize this is old, but figured I would update for ES2015.
The do have a bit more meaning with let + const as found here https://babeljs.io/docs/learn-es2015/
function f() {
{
let x;
{
// okay, block scoped name
const x = "sneaky";
// error, const
x = "foo";
}
// okay, declared with `let`
x = "bar";
// error, already declared in block
let x = "inner";
}
}
I don't understand the reasoning behind the outermost curly braces, but functions are always written inside curly braces in javascript.
If you are working with other developers they may find the outer curly braces more confusing than helpful and there could be some negative effects : https://web.archive.org/web/20160131174505/http://encosia.com/in-javascript-curly-brace-placement-matters-an-example/
There are probably many reasons NOT to write code this way... readability is #1, as most would find this makes the code harder to read. However, there is technically nothing wrong with coding like this, and if it's easier for you to read, then it should be fine.
I think there will be problems with this convention when you start coding more complex programs. There must be a good reason why people don't code like that other than adding extra lines of code.
But since Javascript doesn't have block scoping, the code will still work.
Is defining JavaScript variables inside if-statements correct?
if(a==1){
var b = 1;
} else {
var b = 0;
}
I know the code above will work, however, WebMatrix highlights the variables.
Should I define the variables outside the if-statement? Or the first option's correct? Or it doesn't really matter?
var b = '';
if(a==1){
b = 1;
} else {
b = 0;
}
As of the official release of ES2017 spec (2017-07-08), EcmaScript does support true block scope now using the let or const keywords.
Since ECMAscript doesn't have block scope but function scope, its a very good idea to declare any variable on the top of your function contexts.
Even though you can make variable and function declarations at any point within a function context, it's very confusing and brings some weird headaches if you aren't fully aware of the consequences.
Headache example:
var foo = 10;
function myfunc() {
if (foo > 0) {
var foo = 0;
alert('foo was greater than 0');
} else {
alert('wut?');
}
}
Guess what, we're getting a 'wut?' alert when calling myfunc here. That is because an ECMAscript interpreter will hoist any var statement and function declaration to the top of the context automatically. Basically, foo gets initialized to undefined before the first if statement.
Further reading: JavaScript Scoping and Hoisting
Note that ECMAscript 6 does support block-level variables using the 'let' rather than the 'var' keyword. While variables declared with 'var' are hoisted to be function-scope regardless of where they are declared, those defined using 'let' are scoped to the enclosing block only.
Putting a var inside an if statement is not against "the rules" of the language, but it means that, because of var hoisting, that var will be defined regardless of whether the if statement's condition is satisfied.
Because JavaScript's variables have function-level scope, your first example is effectively redeclaring your variable, which may explain why it is getting highlighted.
On old versions of Firefox, its strict JavaScript mode used to warn about this redeclaration, however one of its developers complained that it cramped his style so the warning was turned off. (Current versions of Firefox support a block-level variable declaration syntax.)
See function four on What is the scope of variables in JavaScript?
As of 2012, there's no block-level scope in JavaScript. So your first version is fine: the variables are defined in the scope outside the if block.