ReferenceError: variable is not defined 1 [duplicate] - javascript

This question already has answers here:
Are variables declared with let or const hoisted?
(7 answers)
Closed 3 years ago.
First off, not a duplicate, I did look at the other "ReferenceErrors" here on SO. However, my problem is not scope related nor a misspelling.
My issue is that copy apparently isn't defined despite I clearly defined it at the very first line. What is going on?
My goal is to redefine the variable copy inside the loop.
var copy = 'test 1';
for (let i = 0; i < 2; i++) {
let t = copy.replace(/(\d+)/, function (fullMatch, n) {return `${Number(n) + 1}`;});
console.log(t); // Output: 'test 2';
let copy = t; // <-- ReferenceError: copy is not defined
}

I guess the issue here is you are trying to declare twice the copy variable in the loop:
let copy = t;
The solution is to remove let from your for loop as the following:
var copy = 'test 1';
for (let i = 0; i < 2; i++) {
let t = copy.replace(/(\d+)/, function (fullMatch, n) {return `${Number(n) + 1}`;});
console.log(t);
copy = t; // removed let
}
So you are not declaring twice the copy variable.
Additionally I recommend to read the What's the difference between using “let” and “var”? titled article.

This is happening because of the temporal dead zone, you declare copy using let which has a block scope so in this case it is limited to the scope of the for loop.
now inside the for-loop you are trying to access copy thinking that it is the outer variable declared using var but it is not so, it is actually referring to the inner copy declared using let.
Variables declared using let are not hoisted to the top and cannot be used before declaration. This phenomenon is known as temporal dead zone.
The same is true for variable declared using const.
for (let i = 0; i < 2; i++) {
let t = copy.replace(/(\d+)/,... //copy is actually not available here, accessing it here will result in a ReferenceError
...
let copy = t; //copy will be available after this declaration
To fix it, don't declare it again with let, just re-assign the outer variable:
(function(){
var copy = 'test 1';
for (let i = 0; i < 2; i++) {
let t = copy.replace(/(\d+)/, function (fullMatch, n) {return `${Number(n) + 1}`;});
console.log(t);
copy = t; //copy is the outer variable
}
})();

This is a Temporal dead zone (TDZ) issue.
In your loop, copy is hoisted to the top of the loop. However, instead of being undefined, like var, it is "not initialized" and accessing it throws an error.
var copy = 'test 1';
for (let i = 0; i < 2; i++) {
// copy is hoisted here
// It is not initialized
//Accessing copy here throws ReferenceError
let t = copy.replace(/(\d+)/, function (fullMatch, n) {return `${Number(n) + 1}`;});
console.log(t); // Output: 'test 2';
let copy = t; // <-- ReferenceError: copy is not defined
}

The reason this is happening is because you declare copy twice but this is only half of the truth.
Look This code will work. Note that you declare the copy twice. But this will not throw will undefined error
var copy = 'test 1';
for (let i = 0; i < 2; i++) {
let copy = t;
}
While this will surely throw an error
var copy = 'test 1';
for (let i = 0; i < 2; i++) {
let t = copy.replace(/(\d+)/, function (fullMatch, n) {return `${Number(n) + 1}`;});
let copy = t;
}
This is because of the let copy. let copy's scope is inside the for loop.
Compiler will always try to find the variable that is closest to the function you called.
hence undefined error will throw. In your case, your trying to call the var copy but the compiler found the let copy that's why it returning error not on the let copy itself but in these lines
let t = copy.replace(/(\d+)/, function (fullMatch, n) {return `${Number(n) + 1}`;});

you can use it like this:
var copy = 'test 1';
for (let i=0; i<2; i++) {
let t = copy.replace(/(\d+)/, function (fullMatch, n) {return `${Number(n) + 1}`;});
console.log(t); // Output: 'test 2';
copy = t; // <-- ReferenceError: copy is not defined
}
Or use a new variable name like: let copy1
To avoid ReferenceError

Related

Can a function read the binding of another function?

I'm currently learning javascript and I would appreciate some help.
I've been playing around trying to create a program that would multiply two numbers without using the * operator. I already found an easier way to do it than my approach but I'd like to know why the code I wrote doesn't work:
function addToItself(a) {
a = a + a;
return a;
}
function times(b) {
for (count = 0; count < b; count++) {
addToItself(a)
}
return a;
}
function multiply (x, y) {
a = x;
times(y);
}
let output = multiply(5, 2);
alert(output);
Is it not working because the binding "a" in the addToItself function has a local scope and the multiply function can't read it or is it something else?
Thanks a lot in advance!
The issue is with the scope of each variable. In JavaScript, a variable declated within a function is scoped to that function. This means that a variable declared within a function can only be accessed within the function. Scopes are nested, so a variable declared globally is accessible inside a function too, though that's often discouraged.
Additionally, function arguments (such as a in addToItself and b in times) are treated like variables scoped to the function.
I would advise looking at the MDN docs for "scope" and familiarizing yourself with how variables are scoped in JavaScript.
I have included a fixed version of your code is below, for reference:
function addToItself(a) {
// I used a new variable here since reassigning to an argument is discouraged
const twoA = a + a;
return twoA;
}
console.log('5 + 5 = ' + addToItself(5));
function times(value, times) {
let temp = 0;
for (let count = 0; count < times; count++) {
temp += value;
}
return temp;
};
console.log('5 * 5 = ' + times(5, 5));
No you can't read variable inside another function, there are easier way, like
function multiply(x, y) {
var result = 0;
for (var count = 0; count < y; count++) {
result += x
}
return result;
}
console.log("5 * 2 = " + multiply(5, 2));

JavaScript: implement let-working with var

So I have a code that clone let-behavior. But I don't undestand how and why it is working. Can someone explain it?
(function() {
var a = 2;
})()
let is scoped to the block it appears in.
var is scoped to the function it appears in.
By replacing a block with a function (which is immediately invoked) var is scoped to the same lines of code as let would be.
Where you can only use var variables but you want to ensure that a variable declaration is not going to overwrite another pre-existing variable of the same name, you can use an IIFE to scope that variable to the function. This in effect creates a "block", similar to the second example below.
var variables are scoped to functions:
var a = 1;
let b = 1;
(function() {
var a = 2;
let b = 1;
})();
console.log(a); //=> 1 (a is not overwritten because the second `var a` is function-scoped)
console.log(b); //=> 1
let variables are scoped to blocks:
let a = 1;
var b = 1;
{
let a = 2;
var b = 2;
}
console.log(a); //=> 1 (a is not overwritten as let is block-scoped)
console.log(b); //=> 2 (b is overwritten as var is not block-scoped)
It's worth mentioning that you can redeclare a var variable, hence why it would be possible to overwrite an existing var variable of the same name. However, you cannot redeclare a let variable:
var a = 1
// will not error as `var` does not prevent redeclaration
var a = 2
let b = 1
{
// will not error as `b` is not declared in this block
let b = 2
}
// will error as `b` is already declared in this scope
let b = 3
You can read more about let and var in this question and its answers.

JSHint warning "Function declared within loop referencing an outer scope variable may lead to confusing semantics" . How can I improve the code?

JSHint shows the error:
"Function declared within loop referencing an outer scope variable may lead to confusing semantics".
How can I improve the following code to get rid of the warning?
var getPrecedence = function getPrecedence(operator, operators) {
var keys = Object.keys(Object(operators));
for (var i = 0, len = keys.length; i < len; i++) {
var check = Object.keys(operators[keys[i]]).some(function (item) {
return item === operator;
});
if (check) return operators[keys[i]][operator];
}
};
You are supposed not to use the function expression inside the loop body, but instead declare it outside:
function getPrecedence(operator, operators) {
function isOperator(item) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
return item === operator;
}
var keys = Object.keys(Object(operators));
for (var i = 0, len = keys.length; i < len; i++) {
var check = Object.keys(operators[keys[i]]).some(isOperator);
// ^^^^^^^^^^
if (check) return operators[keys[i]][operator];
}
}
Of course the whole thing could be simplified by just using includes instead of some, and find instead of the loop:
function getPrecedence(operator, operators) {
var keys = Object.keys(Object(operators));
var opkey = keys.find(key =>
Object.keys(operators[key]).includes(operator)
);
if (opkey) return operators[opkey][operator];
}
And finally, Object.keys(…).includes(…) can be simplified to operator in operators[key].
Add it before calling the function, this one will bypass that check
/* jshint -W083 */

Block scoping with nested var shows different errors

I'm trying to understand block scoping on ES6 and found the following issue (maybe I'm just misunderstanding the concept):
In the first test I tried the following and got the commented error:
{
const x = 2;
console.log( x ); //2
{
let x = "b";
console.log(x); //b
{
var x = true; //Identifier 'x' has already been declared
}
}
}
console.log(x)
But when I try to get the type of the "already declared" x I get :
{
const x = 2;
console.log( x ); //2
{
let x = "b";
console.log(x); //b
{
console.log(typeof x); //this throws Uncaught ReferenceError: x is not defined
}
}
}
console.log(x);
I'll keep trying to see what is going on, any ideas are accepted.
Actually, your error comes from the final console.log(x);. Removing that line makes your code work fine.
This error makes perfect sense. x is only defined in inner blocks; it doesn't exist in the outer scope.
First of all, you need to know the difference between let and var
let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope. An explanation of why the name "let" was chosen can be found here.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
Second, you got this error "Identifier 'x' has already been declared" because you already have "let x" so you can't use "var x". However, if you change the var to let, it will work.
Example to understand scoping:
function test() { // first scope
let x = 1;
let y = 2;
console.log(x, y); //1 2
function test2() { // second scope
let x = 2;
let z = 3;
console.log(x,y,z); //2 2 3
}
test2();
console.log(z); //z is not defined... notice z is defined in the second scope, not the first one
}
test();
Keep in mind, you can access variables from higher/global scopes in inner scopes, but you can't access variables from inner scopes in higher/global scopes.
Read this: What is the scope of variables in JavaScript?
EDIT:
If you do this, it should work fine
const x = 2;
console.log( x ); //2
{
let x = "b";
console.log(x); //b
{
x = true;
}
}
console.log(x)
OR
const x = 2;
console.log( x ); //2
{
let x = "b";
console.log(x); //b
{
let x = true;
}
}
console.log(x)

What happens when a JS closure is made with a block-scope variable?

A little while ago, I asked a question using this sample JS code...
for (var myindex = 0; myindex < mylist.length; myindex += 1) {
var copyindex = myindex;
MyAsync(mylist[myindex], function () { alert(copyindex); });
}
(The answer was essentially to call a named function, which keeps each variable in the loop separate.)
My question for today is, what's actually going on in this sample code above? Each alert call is being passed the same copyindex variable, even though a new one is being declared each time the loop goes around.
Is this behavior required by the JS language? (That by declaring a variable with block scope, I'm really declaring it at the top of the function.)
Or, is this perhaps an implementation detail of the few browsers I tested with, and future JS platforms are under no obligation to link the many instances of copyindex together?
Each alert call is being passed the same copyindex variable, even though a new one is being declared each time...
That's a common misunderstanding. var does not have block scope, so your code really is this:
var copyindex;
for (var myindex = 0; myindex < mylist.length; myindex += 1) {
copyindex = myindex;
MyAsync(mylist[myindex], function () { alert(copyindex); });
}
More (on my blog): Poor, misunderstood var and, of course, in the specification — it's covered in §10.5 - Declaration Binding Instantiation.
ES6 will introduce block-scoped variables via the let keyword. Where you use the let keyword matters quite a lot.
Let's take a simpler example, starting with var:
for (var x = 0; x < 3; ++x) {
setTimeout(function() { console.log("x = " + x); }, 0);
}
console.log("typeof x = " + typeof x);
We know that will give us
number
3
3
3
...because var is hoisted to the top of the scope, and so the same x is used by all three functions we create, and x exists outside the loop. (We see the typeof result first, of course, because the others happen after the minimal timeout.)
If we use let instead, in that same place:
for (let x = 0; x < 3; ++x) {
setTimeout(function() { console.log("x = " + x); }, 0);
}
console.log("typeof x = " + typeof x);
We get
undefined
3
3
3
...because x is scoped to the loop, not loop iterations. All three functions still use the same x, the only difference is that x doesn't exist outside the loop.
But if we use let within the body:
for (let n = 0; n < 3; ++n) {
let x = n;
setTimeout(function() { console.log("x = " + x); }, 0);
}
console.log("typeof x = " + typeof x);
We get
undefined
0
1
2
...because now x is scoped to the body of each iteration, not the loop as a whole.
You can try these yourself using NodeJS, just be sure to give it the --harmony flag to enable ES6 features.

Categories