You have not assigned a new value to the const
variable. Instead, you are assigning a value to the function parameter that shares the same name. Since the function parameter is a non-const copy of the variable, you are allowed to make an assignment to it.
I will consolidate all my comments into a more detailed answer.
In the following code snippet:
"use strict"
const state = "123";
Promise.resolve(state).then(state => {
console.log(state); // `"123"`
state = 456; // reassign `const` variable `state` to `"456"`
return state
}).then(state => console.log(state)) // `"456"`
// not reached
.catch(err => console.error(err.message));
Initially, you define a const state = "123"
variable. Any attempt to change the content of this particular state
variable will result in an exception.
Then, when you execute this:
Promise.resolve(state).then(state => {
This statement creates a .then()
handler function with a single argument named state
. When the .then()
handler is invoked, whatever is passed as the argument to the handler is copied into this new local variable named state
. Function arguments are mutable and can be reassigned.
By having two separate variables with the same name but at different scopes, the function parameter named state
inside the .then()
handler takes precedence over the higher scoped variable with the same name. Within the .then()
handler, when you refer to state
, you are actually accessing the function parameter which is a copy of the original variable passed as an argument to the .then()
handler. Javascript does not have true reference variable types, everything is passed by value.
Moreover, function arguments are not constants, so you can assign new values to them.
Therefore, when you write state = "456";
within the .then()
handler, you are simply updating the function parameter. Due to the naming conflict, you cannot access the outer const state
variable from within the handler, as the interpreter resolves the closest scoped definition of the variable with the given name.
Your confusion might clear up if you avoid creating conflicting variable names. By renaming the parameter to something unique like localState
:
"use strict"
const state = "123";
Promise.resolve(state).then(localState => {
console.log(state); // `"123"`
state = 456; // reassign `const` variable `state` to `"456"`
return state
}).then(state => console.log(state)) // `"456"`
// not reached
.catch(err => console.error(err.message));
In this case, trying to assign a new value to state
will lead to an exception since no conflicting variable has been declared locally with the same name. Therefore, your attempt to assign state = 456
will be recognized as an attempt to modify a const
variable, triggering an error.
To the best of my knowledge, JavaScript lacks a built-in mechanism to prevent shadowing external variables with similarly named local variables. When interpreting variable names, the language searches through nested scopes starting from the innermost scope towards the global scope - causing local definitions to take precedence over external ones. This behavior was designed intentionally for preserving code integrity and preventing unintended modifications affecting unrelated parts of the program. Declaring conflicts with higher scoped variables inadvertently means choosing the local definition over the external one, leading to potential issues if not managed carefully.