function func(x = y, y = 2) {
return [x, y];
}
func(); // ReferenceError: y is not defined
func(1); // [1, 2]
As the code above implied, there is a hidden TDZ in the function parameters scope, which explains why the code below fails:
function func(arg) {
let arg = 1; // SyntaxError: Identifier 'arg' has already been declared
}
So function parameters should be declared with let, but what confused me is :
function func(arg) {
var arg = 1;
console.log(arg); // 1
}
this code works fine.
Why you can redeclare variables using var? How does Javascript declare function parameters?
edit:
I know exactly you can't use let to redeclare a variable. The question here is if function parameters is declared using let, so:
function func(arg) {
var arg = 1;
}
is like:
let arg; // arg parameter declares here
var arg = 1; // func body
and why this can run without an exception?
there is a hidden TDZ in the function parameters scope
Yes indeed. Have a look here for some more examples.
How does Javascript declare function parameters?
As parameters - see here for a step-by-step explanation. They're neither the same as let nor the same as var, they have their own semantics. ES6, which introduced default initialisers, gave them the same TDZ limitations as in let to catch more programmer mistakes.
Why you can redeclare variables using var?
Because until ES5, redeclaring a variable was not an error condition, and this behaviour needed to be preserved to not break the web. It could only be introduced for new features, like let and const - or argument lists that use default initialisers, try function x(bar, bar) {} vs function x(bar, bar=1){}.
This isn't necessarily related to the function's arguments.
This will throw an error as well:
function func() {
var arg = 1;
let arg = 2
console.log(arg); // 1
}
func();
You just can't use let to re-declare a variable, while var isn't that strict: var doesn't try to re-declare the variable if it's already declared. It just assigns the new value.
For more information on the specifics of let, see this MDN page on the let statement.
The reason that is, is because of something that people in the JavaScript community refer to as "hoisting" when you do var arg = 1 that gets translated into var arg; arg = 1; however if var has already been defined as is the case here because of the function parameter, then the compiler tries to be helpful and ignores subsequent re-declarations instead of throwing an error... which is probably an unexpected behaviour, and hasn't been followed through with the new let variable declaration, however, it has to stay there for backwards compatibility reasons.
So var is legacy syntax and ideally you probably want to avoid using it.
Instead using let for variables that can be reassigned, and const for constants.
var also has global scope where as let and const are limited to the scope they are in.
let is added more recently and with it, an error throw was introduced that manifests when you try to declare a variable that involves let twice. This does not happen with var which will simply replace the old value, unless the variable has been already declared with let.
function func(arg) {
let arg = 1; // not ok, because `let` knows that `arg` is being redeclared
}
function func(arg) {
var arg = 1; // ok, because `var` will just replace the value of `arg`
}
function func() {
let arg = 0;
var arg = 1; // not ok, because `let` declared `arg` already
}
Here is a link to the MDN for let which explains the feature.
Parameter bindings does not exactly follow the same behavior as the ones declared with let.
1) Parameter bindings get created before var bindings. var doesn't create bindings that already exist, so there's never a problem there. let however creates bindings after var.
2) There's an early error on all productions that create a scope that has it as a rule that no lexical binding can occur when there's already a variable declaration with the same name.
The first example will not throw an error a value if an argument is passed to func call.
var does not have the same scope as let or const.
Related
This question already has answers here:
What is the temporal dead zone?
(3 answers)
Closed 2 years ago.
function pickColor() {
var random = Math.floor(Math.random() * colors.length);
return colors[random];
}
let pickColor = () => {
var random = Math.floor(Math.random() * colors.length);
return colors[random];
}
when I try and call the second one, I get an error that says "Cannot access 'pickColor' before initialization"
This is due something called "hoisting".
Basically when you use function, JavaScript moves the function to the top of the scope, so you can access it before initialization. Using let does not do this.
func_1();
function func_1() {
console.log("Thing");
}
func_2(); // will cause an error
let func_2 = () => console.log("Thing");
Details: Everything is technically hoisted, but let and const don't initialise until you get to their line. If you used var, the variable would start off as undefined
console.log(aa); //undefined
var aa = "hello";
console.log(aa); //hello
console.log(bb) // error
let bb = "hello";
Sidenotes (This part is not a solution to the above problem):
1. You should use const instead of let because I don't imagine you will need to change the value of a function.
2. Another difference between these declarations, is the value of the keyword this (it's the same in this situation but it can be different). I won't explain it here, you will probably come across it if you do more Javascript so worth looking into.
let pickColor = ... acts like a normal variable declaration + assignment.
The assignment = is done only when the actual line of code is executed.
You can call the function defined this way only if the call happens after the declaration, and in a visible scope.
On the contrary, function() definitions are completely 'hoisted', meaning they act like if they were defined on top of the JS block, and may be called "before" their definition.
Example inspired from http://adripofjavascript.com/blog/drips/variable-and-function-hoisting.html :
isItHoisted();
function isItHoisted() {
console.log("Yes! This function declaration acts the same as if it was declared _before_ the call.");
}
isThisOtherOneHoisted(); // throws an ReferenceError if used before it is assigned
let isThisOtherOneHoisted = () => console.log("Javascript sees that the name exists, but the assignment has not been done.");
/**
like :
There is this variable isThisOtherOneHoisted defined later, just so you know.
isThisOtherOneHoisted(); // error, variable does not even containt a function yet
isThisOtherOneHoisted = () => console.log(...)
*/
As additional details, javascript still "see" that it is used before it is intitialized, so that's why the error message is different than if you used a variable that doesn't exist at all.
The declaration is hoisted for any variable, but only the fact that the variable exist. The assignments with = is only executed where it is written.
The function declaration
function pickColor(){...}
is moved to the top of the scope (hoisted first) along with any variables declared with var.
where as function expression declared with let is only accessed when the interpreter comes across that line of code.
Example -
let two = () => {
console.log('something else');
}
var x = 'demo'
let y = 'demo123'
function one() {
console.log('something');
};
function three(){
one();
}
variable x , function one(){...} , function three(){...} are moved to the top of the scope as soon as the execution starts,
x is undefined until the line
x = 'demo'
then
x is assigned a value of 'demo'.
Variable y is not initiated or assigned any value until the line
let y = 'demo123'
since let is used to initiate the variable, same goes for the function expression initiated with let.
Why does var allow duplicate declaration but why does const and let not allow duplicate declaration?
var is allow duplicate declaration
xx=1;
xx=2;
console.log(xx+xx);//4
var xx=1;
var xx=2;
console.log(xx+xx);//4
But let and const does not allow duplicate deceleration
const yy=1;
const yy=2;
console.log(yy+yy);//Uncaught SyntaxError: Identifier 'yy' has already been declared",
let zz=1;
let zz=2;
console.log(zz+zz);//Uncaught SyntaxError: Identifier 'zz' has already been declared",
I saw something about that in here like,
Assuming strict mode, var will let you re-declare the same variable in the same scope. On the other hand, let will not.
But I want to know Why let and const doesn't allow re-declaration? and why var does? and How JavaScript handle these three type of deceleration ?
var
The var keyword was the only way to define variables until 2016*.
No matter where you write var x, the variable x is treated as if it were declared at the top of the enclosing scope (scope for var is "a function").
All declarations of the variable within the same scope are effectively talking about the same variable.
Here is an example... you might think that within the function we overwrite the outer name with fenton, and add Fenton to the inner variable...
var name = 'Ramesh';
function myFunc() {
name = 'fenton';
var name = 'Fenton';
alert(name);
}
myFunc();
alert(name);
In fact, it works just like this... the outer variable is not affected by the inner variable thanks to hoisting.
var name = 'Ramesh';
function myFunc() {
var name;
name = 'fenton';
name = 'Fenton';
alert(name);
}
myFunc();
alert(name);
Actually, you could also declare them implicitly by not using the var keyword at all, in which case they would be added to the global scope. Subtle bugs were often tracked to this.
let and const
Both let and const are block-scoped, not function-scoped. This makes them work like variables in most other C-like languages. It turns out this is just less confusing than function-scoped variables.
They are also both "more disciplined". They should be declared just once within a block.
The const keyword also disallows subsequent assignments - so you have to declare it with an assignment (i.e. you can't just write const x, you have to write const x = 'Fenton') - and you can't assign another value later.
Some people think this makes the value immutable, but this is a mistake as the value can mutate, as shown below:
const x = [];
// I can mutate even though I can't re-assign
x.push('Fenton');
// x is now ['Fenton']
Why Does it Matter?
If you want to avoid some of the more confusing aspects of var, such as multiple declarations all contributing to the same hoisted variable, and function-scope, you should use the newer const and let keywords.
I recommend using const as your default keyword, and upgrade it to let only in cases where you choose to allow re-assignment.
Unlike var, let is an ES2015 specification. The specification says:
Redeclaring the same variable within the same function or block scope raises a SyntaxError.
This is to improve scoping over vanilla var.
why does const and let not allow duplicate declaration?
There's a big difference between how c# or java (for example) handle duplicate variable names, where name collision returns a compilation error, and how it works in an interpreted language like js. Please, check the snippet below: The value of i isn't duplicated? Not really, still, in the function and block context the same variable name is referred as two different variables, depending on where those are declared.
function checkLetDuplication() {
let i = 'function scope';
for ( let i = 0 ; i < 3 ; i++ )
{
console.log('(for statement scope): inside the for loop i, equals: ', i);
}
console.log('("checkLetDuplication" function scope): outside the for loop i , equals: ', i);
}
checkLetDuplication();
Assuming you want to know whether this behavior is as per spec, check this 13.3.2
Within the scope of any VariableEnvironment a common BindingIdentifier
may appear in more than one VariableDeclaration but those declarations
collective define only one variable.
let and const are the recent editions, while var is probably as old as Javascript itself.
In old days Javascript code-base didn't used to be too big to bother about programming mistakes and most probably focus was to ensure that instead of reporting the error of re-declaration of variable JS engine should handle it.
Why does var allow duplicate declaration but why does const and let not allow duplicate declaration?
var is allow duplicate declaration
xx=1;
xx=2;
console.log(xx+xx);//4
var xx=1;
var xx=2;
console.log(xx+xx);//4
But let and const does not allow duplicate deceleration
const yy=1;
const yy=2;
console.log(yy+yy);//Uncaught SyntaxError: Identifier 'yy' has already been declared",
let zz=1;
let zz=2;
console.log(zz+zz);//Uncaught SyntaxError: Identifier 'zz' has already been declared",
I saw something about that in here like,
Assuming strict mode, var will let you re-declare the same variable in the same scope. On the other hand, let will not.
But I want to know Why let and const doesn't allow re-declaration? and why var does? and How JavaScript handle these three type of deceleration ?
var
The var keyword was the only way to define variables until 2016*.
No matter where you write var x, the variable x is treated as if it were declared at the top of the enclosing scope (scope for var is "a function").
All declarations of the variable within the same scope are effectively talking about the same variable.
Here is an example... you might think that within the function we overwrite the outer name with fenton, and add Fenton to the inner variable...
var name = 'Ramesh';
function myFunc() {
name = 'fenton';
var name = 'Fenton';
alert(name);
}
myFunc();
alert(name);
In fact, it works just like this... the outer variable is not affected by the inner variable thanks to hoisting.
var name = 'Ramesh';
function myFunc() {
var name;
name = 'fenton';
name = 'Fenton';
alert(name);
}
myFunc();
alert(name);
Actually, you could also declare them implicitly by not using the var keyword at all, in which case they would be added to the global scope. Subtle bugs were often tracked to this.
let and const
Both let and const are block-scoped, not function-scoped. This makes them work like variables in most other C-like languages. It turns out this is just less confusing than function-scoped variables.
They are also both "more disciplined". They should be declared just once within a block.
The const keyword also disallows subsequent assignments - so you have to declare it with an assignment (i.e. you can't just write const x, you have to write const x = 'Fenton') - and you can't assign another value later.
Some people think this makes the value immutable, but this is a mistake as the value can mutate, as shown below:
const x = [];
// I can mutate even though I can't re-assign
x.push('Fenton');
// x is now ['Fenton']
Why Does it Matter?
If you want to avoid some of the more confusing aspects of var, such as multiple declarations all contributing to the same hoisted variable, and function-scope, you should use the newer const and let keywords.
I recommend using const as your default keyword, and upgrade it to let only in cases where you choose to allow re-assignment.
Unlike var, let is an ES2015 specification. The specification says:
Redeclaring the same variable within the same function or block scope raises a SyntaxError.
This is to improve scoping over vanilla var.
why does const and let not allow duplicate declaration?
There's a big difference between how c# or java (for example) handle duplicate variable names, where name collision returns a compilation error, and how it works in an interpreted language like js. Please, check the snippet below: The value of i isn't duplicated? Not really, still, in the function and block context the same variable name is referred as two different variables, depending on where those are declared.
function checkLetDuplication() {
let i = 'function scope';
for ( let i = 0 ; i < 3 ; i++ )
{
console.log('(for statement scope): inside the for loop i, equals: ', i);
}
console.log('("checkLetDuplication" function scope): outside the for loop i , equals: ', i);
}
checkLetDuplication();
Assuming you want to know whether this behavior is as per spec, check this 13.3.2
Within the scope of any VariableEnvironment a common BindingIdentifier
may appear in more than one VariableDeclaration but those declarations
collective define only one variable.
let and const are the recent editions, while var is probably as old as Javascript itself.
In old days Javascript code-base didn't used to be too big to bother about programming mistakes and most probably focus was to ensure that instead of reporting the error of re-declaration of variable JS engine should handle it.
Why does the code below doesn't throw a warning/error about variable re-assignment?
I spent a good amount of time debugging something like this because of the trailing comma.
When the code inside the else statement is moved outside of the if scope, it throws the proper error.
Any explanation would be great.
let a
if (false) {
// Nothing
} else {
const b = 100,
a = 10
}
console.log(a)
// undefined
Let and const variables are not hoisted and are created in block scope.
So in your code, you are creating another variable a which is a const. and once scope is over, it will again point to a that is outside.
For example, if you try to reassign it in the same block, it will throw error as you cannot change value of const.
let a
if (false) {
// Nothing
} else {
const b = 100,
a = 10
a = 2;
}
console.log(a)
// undefined
As pointed out by Bergi, all variables are hoisted. But they are hoisted in a way to give impression that they are not hoisted. You can read full description here: Are variables declared with let or const not hoisted in ES6?
Why does the code not throw a warning/error about variable re-assignment?
Because you are not re-assigning a variable (which is what you actually wanted, assign the let a variable), you are re-declaring it with that const - and that in a different scope (in this case, the else block). If you had re-declared it in the same scope (e.g. let a; const a = 5) it would have thrown an error indeed. A shadowing declaration (that hides an existing names) in an inner scope is not an error, it is in fact necessary for local scopes to work and not be affected by their surroundings.
If you want to get a warning about this, use a linter that has a rule against shadowing declarations, e.g. in ESLint.
In else condition, a is re declared as const.So whatever the value (u assigned 10) within in else condition. you have to put ; at end of b declaration. otherwise a will be declared as const.So, a value is undefined in the outside of else condition.
let a
if (false) {
// Nothing
} else {
const b = 100;
a = 10
}
console.log(a)
in that case, it is because it never gets into the else statement, it is always false.
I usually use alert(string); to debug and know the value of variables, try one at the beginning of the if, or use console.log()
I also recommend you to read the following regarding let or var:
What's the difference between using "let" and "var" to declare a variable?.
No matter whether I define the function after the variable
var a = 1;
function a() {};
typeof a // number
Or if I define the function before the variable
function a() {};
var a = 1;
typeof a // number
the final typeof result is always number
I found some explanation about execution context in http://davidshariff.com/blog/what-is-the-execution-context-in-javascript/
Before executing the function code, create the execution context.
......
Scan the context for variable declarations:
If the variable name already exists in the variable object, do nothing and continue scanning.
but this does not seem to work.
So how can I explain it?
It's to do with JavaScript's variable hoisting. Try this instead:
var a = 1;
var a = function() {};
typeof a // function
You're implicitly declaring the variable multiple times by using the function statement 'function a() {};', which as noted by others hoists the variable and behaves unexpectedly due to the order that the browser registers the declarations.
Behind the scenes, this statement instantiates a function object and assigns the result to the variable passed as the function name (reference) but this is done before the explicit var declarations are performed, and so that overrides the implicit declaration. If you just do the following, it will work more intuitively:
var a = 1;
a = function(){};
console.log(typeof a); // function
This is a better option than the multiple var declaration in the other answer from a logical standpoint because (even though you can), it's not a good practice to declare the variable multiple times anyway.
To specifically answer the 'why' for this question: it's so that you can use these kinds of statements to define functions and use them in your explicit declarations, as in
var a = someFunction();
function someFunction(){ return 'someVal'; }
If the function statements weren't parsed and hoisted first, this wouldn't be possible.
As already mentioned, it has to do with the way JavaScript hoisting works. The main issue to notice is that JavaScript will hoist the complete function definition (along with the function body) up to the top, but keeps the variable initialization where it is (only the declaration is hoisted).
So if you write this:
var a = 1;
function a () {
}
it will be translated to:
var a;
function a() {
}
a = 1;
and if you write this:
function a () {
}
var a = 1;
it will be translated to:
function a () {
}
var a;
a = 1;
So no matter what you do, a = 1; will remain at the very bottom.
Please note that the above "translations" should be seen theoretically. JavaScript probably has a way to omit the var a; statement if there is already a function declaration with the same name. And there also might be a defined order (functions get hoisted before variables or the other way around). But all of this doesn't affect the outcome of the variable initialization being the only part that is NOT hoisted at all.