Test whether variable declared yet in containing scope
up vote
1
down vote
favorite
I have an inner function that references a variable initialized by its containing outer function:
function outer() {
function inner() {
if (foo) { ... }
}
let foo = 'bar';
inner(); // no error
};
However, there are some circumstances where the inner function may be invoked before the statement defining foo
has executed. In this case, any reference to foo
causes a ReferenceError in the inner function:
function outer() {
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
This is surprising to me, given that foo
has block scope, and I am within the enclosing block when executing the inner function.
More surprisingly is that even attempting to detect this situation with the typeof
operator - which I always understood to be a safe way to test for undefined variables - causes the same ReferenceError:
function outer() {
function inner() {
if (typeof foo !== 'undefined') { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
Edit: I now understand that this behavior is the result of the "temporal dead zone" involving let
and const
variables discussed elsewhere. However, I'm still looking for a clean, safe way to handle the situation.
Is there any safe way to test whether a block-scoped variable (e.g., one created with 'let' or 'const') has yet reached its declaration?
javascript typeof
|
show 1 more comment
up vote
1
down vote
favorite
I have an inner function that references a variable initialized by its containing outer function:
function outer() {
function inner() {
if (foo) { ... }
}
let foo = 'bar';
inner(); // no error
};
However, there are some circumstances where the inner function may be invoked before the statement defining foo
has executed. In this case, any reference to foo
causes a ReferenceError in the inner function:
function outer() {
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
This is surprising to me, given that foo
has block scope, and I am within the enclosing block when executing the inner function.
More surprisingly is that even attempting to detect this situation with the typeof
operator - which I always understood to be a safe way to test for undefined variables - causes the same ReferenceError:
function outer() {
function inner() {
if (typeof foo !== 'undefined') { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
Edit: I now understand that this behavior is the result of the "temporal dead zone" involving let
and const
variables discussed elsewhere. However, I'm still looking for a clean, safe way to handle the situation.
Is there any safe way to test whether a block-scoped variable (e.g., one created with 'let' or 'const') has yet reached its declaration?
javascript typeof
Use this keyword => this.foo to make sure the variable declared or not.
– Nattamai Jawaharlal Manikandan
Nov 11 at 20:51
@NattamaiJawaharlalManikandan The propertythis.foo
would be something completely distinct from the variable defined in this scope.
– Myk Willis
Nov 11 at 21:50
"Is there any safe way to test whether a block-scoped variable has yet reached its initialisation?" - no, not really, except fortry
/catch
. But what would you need this for? Either you will want to use the variable or you don't. If it throws an error because you try to access it before it is initialised, don't work around it but just fix your mistake - callinginner()
in the last line.
– Bergi
Nov 11 at 22:29
@Bergi The reason this is needed in "real" code is because inner() is an error handling function, and outer() is a request processing function. In inner() (the error handling function), I want to gather all of the context information collected by outer() so that I can write it to an error response.foo
, in the real world, is a variable that is the result of parsing input values to outer(), but there are conditions where we encounter an error beforefoo
is initialized. Thus, inner() may have to be called before foo is initialized.
– Myk Willis
Nov 13 at 6:40
@MykWillis Can you make an example of how you are detecting the error? Is it atry
-catch
? I'm unclear what the exact scope ofinner
andfoo
would be, wouldn't they be nested in different blocks? Also for what you described, I would recommend to simply usevar
(or put thelet
declarations at the top of your scope).
– Bergi
Nov 13 at 8:39
|
show 1 more comment
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I have an inner function that references a variable initialized by its containing outer function:
function outer() {
function inner() {
if (foo) { ... }
}
let foo = 'bar';
inner(); // no error
};
However, there are some circumstances where the inner function may be invoked before the statement defining foo
has executed. In this case, any reference to foo
causes a ReferenceError in the inner function:
function outer() {
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
This is surprising to me, given that foo
has block scope, and I am within the enclosing block when executing the inner function.
More surprisingly is that even attempting to detect this situation with the typeof
operator - which I always understood to be a safe way to test for undefined variables - causes the same ReferenceError:
function outer() {
function inner() {
if (typeof foo !== 'undefined') { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
Edit: I now understand that this behavior is the result of the "temporal dead zone" involving let
and const
variables discussed elsewhere. However, I'm still looking for a clean, safe way to handle the situation.
Is there any safe way to test whether a block-scoped variable (e.g., one created with 'let' or 'const') has yet reached its declaration?
javascript typeof
I have an inner function that references a variable initialized by its containing outer function:
function outer() {
function inner() {
if (foo) { ... }
}
let foo = 'bar';
inner(); // no error
};
However, there are some circumstances where the inner function may be invoked before the statement defining foo
has executed. In this case, any reference to foo
causes a ReferenceError in the inner function:
function outer() {
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
This is surprising to me, given that foo
has block scope, and I am within the enclosing block when executing the inner function.
More surprisingly is that even attempting to detect this situation with the typeof
operator - which I always understood to be a safe way to test for undefined variables - causes the same ReferenceError:
function outer() {
function inner() {
if (typeof foo !== 'undefined') { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
Edit: I now understand that this behavior is the result of the "temporal dead zone" involving let
and const
variables discussed elsewhere. However, I'm still looking for a clean, safe way to handle the situation.
Is there any safe way to test whether a block-scoped variable (e.g., one created with 'let' or 'const') has yet reached its declaration?
javascript typeof
javascript typeof
edited Nov 11 at 21:08
asked Nov 11 at 20:48
Myk Willis
5,83011936
5,83011936
Use this keyword => this.foo to make sure the variable declared or not.
– Nattamai Jawaharlal Manikandan
Nov 11 at 20:51
@NattamaiJawaharlalManikandan The propertythis.foo
would be something completely distinct from the variable defined in this scope.
– Myk Willis
Nov 11 at 21:50
"Is there any safe way to test whether a block-scoped variable has yet reached its initialisation?" - no, not really, except fortry
/catch
. But what would you need this for? Either you will want to use the variable or you don't. If it throws an error because you try to access it before it is initialised, don't work around it but just fix your mistake - callinginner()
in the last line.
– Bergi
Nov 11 at 22:29
@Bergi The reason this is needed in "real" code is because inner() is an error handling function, and outer() is a request processing function. In inner() (the error handling function), I want to gather all of the context information collected by outer() so that I can write it to an error response.foo
, in the real world, is a variable that is the result of parsing input values to outer(), but there are conditions where we encounter an error beforefoo
is initialized. Thus, inner() may have to be called before foo is initialized.
– Myk Willis
Nov 13 at 6:40
@MykWillis Can you make an example of how you are detecting the error? Is it atry
-catch
? I'm unclear what the exact scope ofinner
andfoo
would be, wouldn't they be nested in different blocks? Also for what you described, I would recommend to simply usevar
(or put thelet
declarations at the top of your scope).
– Bergi
Nov 13 at 8:39
|
show 1 more comment
Use this keyword => this.foo to make sure the variable declared or not.
– Nattamai Jawaharlal Manikandan
Nov 11 at 20:51
@NattamaiJawaharlalManikandan The propertythis.foo
would be something completely distinct from the variable defined in this scope.
– Myk Willis
Nov 11 at 21:50
"Is there any safe way to test whether a block-scoped variable has yet reached its initialisation?" - no, not really, except fortry
/catch
. But what would you need this for? Either you will want to use the variable or you don't. If it throws an error because you try to access it before it is initialised, don't work around it but just fix your mistake - callinginner()
in the last line.
– Bergi
Nov 11 at 22:29
@Bergi The reason this is needed in "real" code is because inner() is an error handling function, and outer() is a request processing function. In inner() (the error handling function), I want to gather all of the context information collected by outer() so that I can write it to an error response.foo
, in the real world, is a variable that is the result of parsing input values to outer(), but there are conditions where we encounter an error beforefoo
is initialized. Thus, inner() may have to be called before foo is initialized.
– Myk Willis
Nov 13 at 6:40
@MykWillis Can you make an example of how you are detecting the error? Is it atry
-catch
? I'm unclear what the exact scope ofinner
andfoo
would be, wouldn't they be nested in different blocks? Also for what you described, I would recommend to simply usevar
(or put thelet
declarations at the top of your scope).
– Bergi
Nov 13 at 8:39
Use this keyword => this.foo to make sure the variable declared or not.
– Nattamai Jawaharlal Manikandan
Nov 11 at 20:51
Use this keyword => this.foo to make sure the variable declared or not.
– Nattamai Jawaharlal Manikandan
Nov 11 at 20:51
@NattamaiJawaharlalManikandan The property
this.foo
would be something completely distinct from the variable defined in this scope.– Myk Willis
Nov 11 at 21:50
@NattamaiJawaharlalManikandan The property
this.foo
would be something completely distinct from the variable defined in this scope.– Myk Willis
Nov 11 at 21:50
"Is there any safe way to test whether a block-scoped variable has yet reached its initialisation?" - no, not really, except for
try
/catch
. But what would you need this for? Either you will want to use the variable or you don't. If it throws an error because you try to access it before it is initialised, don't work around it but just fix your mistake - calling inner()
in the last line.– Bergi
Nov 11 at 22:29
"Is there any safe way to test whether a block-scoped variable has yet reached its initialisation?" - no, not really, except for
try
/catch
. But what would you need this for? Either you will want to use the variable or you don't. If it throws an error because you try to access it before it is initialised, don't work around it but just fix your mistake - calling inner()
in the last line.– Bergi
Nov 11 at 22:29
@Bergi The reason this is needed in "real" code is because inner() is an error handling function, and outer() is a request processing function. In inner() (the error handling function), I want to gather all of the context information collected by outer() so that I can write it to an error response.
foo
, in the real world, is a variable that is the result of parsing input values to outer(), but there are conditions where we encounter an error before foo
is initialized. Thus, inner() may have to be called before foo is initialized.– Myk Willis
Nov 13 at 6:40
@Bergi The reason this is needed in "real" code is because inner() is an error handling function, and outer() is a request processing function. In inner() (the error handling function), I want to gather all of the context information collected by outer() so that I can write it to an error response.
foo
, in the real world, is a variable that is the result of parsing input values to outer(), but there are conditions where we encounter an error before foo
is initialized. Thus, inner() may have to be called before foo is initialized.– Myk Willis
Nov 13 at 6:40
@MykWillis Can you make an example of how you are detecting the error? Is it a
try
-catch
? I'm unclear what the exact scope of inner
and foo
would be, wouldn't they be nested in different blocks? Also for what you described, I would recommend to simply use var
(or put the let
declarations at the top of your scope).– Bergi
Nov 13 at 8:39
@MykWillis Can you make an example of how you are detecting the error? Is it a
try
-catch
? I'm unclear what the exact scope of inner
and foo
would be, wouldn't they be nested in different blocks? Also for what you described, I would recommend to simply use var
(or put the let
declarations at the top of your scope).– Bergi
Nov 13 at 8:39
|
show 1 more comment
2 Answers
2
active
oldest
votes
up vote
2
down vote
accepted
One brute-force approach is to simply catch the exception thrown by typeof
in the "temporal dead zone" before foo
has been initialized:
function outer() {
function inner() {
let fooExists = false;
try { fooExists = typeof foo !== 'undefined' } catch(e) {}
if (fooExists) { /* ... */ }
}
inner(); // no error
let foo = 'bar';
}
It is also possible to use var
instead of let
to work around this issue. Because var
is function-scoped, the declaration will be hoisted to the top of outer
, making the variable available (though undefined) at all times outer is executing:
function outer() {
// due to hoisting, there is a logical `var foo;` here
function inner() {
if (typeof foo !== 'undefined') { /* ... */ }
}
inner(); // no error; `foo` has been hoisted
var foo = 'bar';
}
A similar approach could be taken by putting the declaration (but not initialization) of foo
at the top of the outer function while still using let
:
function outer() {
let foo;
function inner() {
if (typeof foo !== 'undefined') { /* ... */ }
}
inner(); // no error
foo = 'bar';
}
This last solution would seem to be the cleanest solution for the example given in the question. Unfortunately, it cannot be used when using const
variables.
add a comment |
up vote
-1
down vote
function outer() {
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
This is surprising to me, given that foo has block scope, and I am
within the enclosing block when executing the inner function.
Because of hoisting, the declaration for foo
is hoisted to the top of the block, but not the initialization of the variable. It executes as if you had written:
function outer() {
let foo; // declaration only, so still undefined value
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
foo = 'bar'; // Not initialized until after you call inner!
};
Simply move the initialization up in the block and it will work:
function outer() {
let foo = 'bar'; // declared and initialized
function inner() {
if (foo) { ... } // works as expected
}
inner();
};
2
The declaration offoo
is apparently not logically moved to the top of the outer() function (because if it was,typeof foo
would return 'undefined' without an exception). This would be the behavior if I had usedvar
to definefoo
, but in the case oflet
(andconst
) it is apparently not so. e.g., stackoverflow.com/a/31222689/925478
– Myk Willis
Nov 11 at 21:42
And as for moving the initialization to the top of outer(), that is unfortunately not possible in the actual production code I have, which initializesfoo
based on a function call that is only performed after a bunch of preprocessing work. e.g., we don't know the value to which it should be initialized until far down in the function.
– Myk Willis
Nov 11 at 21:43
2
"It executes as if you had written" - This is incorrect. One will give an error, the other will not.
– Spencer Wieczorek
Nov 11 at 21:43
@MykWillis The declaration of foo is moved to the top of the block (hoisted) as referenced by your link as well as here. It's just thatlet
scoped variables have a "temporal dead zone" which prevents access to them prior to initialization.
– Scott Marcus
Nov 11 at 22:17
@SpencerWieczorek I meant it processes logically as if it was written the way I showed. In other words, it's hoisted. The fact that it throws an error doesn't preclude hoisting.
– Scott Marcus
Nov 11 at 22:18
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53253082%2ftest-whether-variable-declared-yet-in-containing-scope%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
One brute-force approach is to simply catch the exception thrown by typeof
in the "temporal dead zone" before foo
has been initialized:
function outer() {
function inner() {
let fooExists = false;
try { fooExists = typeof foo !== 'undefined' } catch(e) {}
if (fooExists) { /* ... */ }
}
inner(); // no error
let foo = 'bar';
}
It is also possible to use var
instead of let
to work around this issue. Because var
is function-scoped, the declaration will be hoisted to the top of outer
, making the variable available (though undefined) at all times outer is executing:
function outer() {
// due to hoisting, there is a logical `var foo;` here
function inner() {
if (typeof foo !== 'undefined') { /* ... */ }
}
inner(); // no error; `foo` has been hoisted
var foo = 'bar';
}
A similar approach could be taken by putting the declaration (but not initialization) of foo
at the top of the outer function while still using let
:
function outer() {
let foo;
function inner() {
if (typeof foo !== 'undefined') { /* ... */ }
}
inner(); // no error
foo = 'bar';
}
This last solution would seem to be the cleanest solution for the example given in the question. Unfortunately, it cannot be used when using const
variables.
add a comment |
up vote
2
down vote
accepted
One brute-force approach is to simply catch the exception thrown by typeof
in the "temporal dead zone" before foo
has been initialized:
function outer() {
function inner() {
let fooExists = false;
try { fooExists = typeof foo !== 'undefined' } catch(e) {}
if (fooExists) { /* ... */ }
}
inner(); // no error
let foo = 'bar';
}
It is also possible to use var
instead of let
to work around this issue. Because var
is function-scoped, the declaration will be hoisted to the top of outer
, making the variable available (though undefined) at all times outer is executing:
function outer() {
// due to hoisting, there is a logical `var foo;` here
function inner() {
if (typeof foo !== 'undefined') { /* ... */ }
}
inner(); // no error; `foo` has been hoisted
var foo = 'bar';
}
A similar approach could be taken by putting the declaration (but not initialization) of foo
at the top of the outer function while still using let
:
function outer() {
let foo;
function inner() {
if (typeof foo !== 'undefined') { /* ... */ }
}
inner(); // no error
foo = 'bar';
}
This last solution would seem to be the cleanest solution for the example given in the question. Unfortunately, it cannot be used when using const
variables.
add a comment |
up vote
2
down vote
accepted
up vote
2
down vote
accepted
One brute-force approach is to simply catch the exception thrown by typeof
in the "temporal dead zone" before foo
has been initialized:
function outer() {
function inner() {
let fooExists = false;
try { fooExists = typeof foo !== 'undefined' } catch(e) {}
if (fooExists) { /* ... */ }
}
inner(); // no error
let foo = 'bar';
}
It is also possible to use var
instead of let
to work around this issue. Because var
is function-scoped, the declaration will be hoisted to the top of outer
, making the variable available (though undefined) at all times outer is executing:
function outer() {
// due to hoisting, there is a logical `var foo;` here
function inner() {
if (typeof foo !== 'undefined') { /* ... */ }
}
inner(); // no error; `foo` has been hoisted
var foo = 'bar';
}
A similar approach could be taken by putting the declaration (but not initialization) of foo
at the top of the outer function while still using let
:
function outer() {
let foo;
function inner() {
if (typeof foo !== 'undefined') { /* ... */ }
}
inner(); // no error
foo = 'bar';
}
This last solution would seem to be the cleanest solution for the example given in the question. Unfortunately, it cannot be used when using const
variables.
One brute-force approach is to simply catch the exception thrown by typeof
in the "temporal dead zone" before foo
has been initialized:
function outer() {
function inner() {
let fooExists = false;
try { fooExists = typeof foo !== 'undefined' } catch(e) {}
if (fooExists) { /* ... */ }
}
inner(); // no error
let foo = 'bar';
}
It is also possible to use var
instead of let
to work around this issue. Because var
is function-scoped, the declaration will be hoisted to the top of outer
, making the variable available (though undefined) at all times outer is executing:
function outer() {
// due to hoisting, there is a logical `var foo;` here
function inner() {
if (typeof foo !== 'undefined') { /* ... */ }
}
inner(); // no error; `foo` has been hoisted
var foo = 'bar';
}
A similar approach could be taken by putting the declaration (but not initialization) of foo
at the top of the outer function while still using let
:
function outer() {
let foo;
function inner() {
if (typeof foo !== 'undefined') { /* ... */ }
}
inner(); // no error
foo = 'bar';
}
This last solution would seem to be the cleanest solution for the example given in the question. Unfortunately, it cannot be used when using const
variables.
edited Nov 11 at 21:27
answered Nov 11 at 21:04
Myk Willis
5,83011936
5,83011936
add a comment |
add a comment |
up vote
-1
down vote
function outer() {
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
This is surprising to me, given that foo has block scope, and I am
within the enclosing block when executing the inner function.
Because of hoisting, the declaration for foo
is hoisted to the top of the block, but not the initialization of the variable. It executes as if you had written:
function outer() {
let foo; // declaration only, so still undefined value
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
foo = 'bar'; // Not initialized until after you call inner!
};
Simply move the initialization up in the block and it will work:
function outer() {
let foo = 'bar'; // declared and initialized
function inner() {
if (foo) { ... } // works as expected
}
inner();
};
2
The declaration offoo
is apparently not logically moved to the top of the outer() function (because if it was,typeof foo
would return 'undefined' without an exception). This would be the behavior if I had usedvar
to definefoo
, but in the case oflet
(andconst
) it is apparently not so. e.g., stackoverflow.com/a/31222689/925478
– Myk Willis
Nov 11 at 21:42
And as for moving the initialization to the top of outer(), that is unfortunately not possible in the actual production code I have, which initializesfoo
based on a function call that is only performed after a bunch of preprocessing work. e.g., we don't know the value to which it should be initialized until far down in the function.
– Myk Willis
Nov 11 at 21:43
2
"It executes as if you had written" - This is incorrect. One will give an error, the other will not.
– Spencer Wieczorek
Nov 11 at 21:43
@MykWillis The declaration of foo is moved to the top of the block (hoisted) as referenced by your link as well as here. It's just thatlet
scoped variables have a "temporal dead zone" which prevents access to them prior to initialization.
– Scott Marcus
Nov 11 at 22:17
@SpencerWieczorek I meant it processes logically as if it was written the way I showed. In other words, it's hoisted. The fact that it throws an error doesn't preclude hoisting.
– Scott Marcus
Nov 11 at 22:18
add a comment |
up vote
-1
down vote
function outer() {
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
This is surprising to me, given that foo has block scope, and I am
within the enclosing block when executing the inner function.
Because of hoisting, the declaration for foo
is hoisted to the top of the block, but not the initialization of the variable. It executes as if you had written:
function outer() {
let foo; // declaration only, so still undefined value
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
foo = 'bar'; // Not initialized until after you call inner!
};
Simply move the initialization up in the block and it will work:
function outer() {
let foo = 'bar'; // declared and initialized
function inner() {
if (foo) { ... } // works as expected
}
inner();
};
2
The declaration offoo
is apparently not logically moved to the top of the outer() function (because if it was,typeof foo
would return 'undefined' without an exception). This would be the behavior if I had usedvar
to definefoo
, but in the case oflet
(andconst
) it is apparently not so. e.g., stackoverflow.com/a/31222689/925478
– Myk Willis
Nov 11 at 21:42
And as for moving the initialization to the top of outer(), that is unfortunately not possible in the actual production code I have, which initializesfoo
based on a function call that is only performed after a bunch of preprocessing work. e.g., we don't know the value to which it should be initialized until far down in the function.
– Myk Willis
Nov 11 at 21:43
2
"It executes as if you had written" - This is incorrect. One will give an error, the other will not.
– Spencer Wieczorek
Nov 11 at 21:43
@MykWillis The declaration of foo is moved to the top of the block (hoisted) as referenced by your link as well as here. It's just thatlet
scoped variables have a "temporal dead zone" which prevents access to them prior to initialization.
– Scott Marcus
Nov 11 at 22:17
@SpencerWieczorek I meant it processes logically as if it was written the way I showed. In other words, it's hoisted. The fact that it throws an error doesn't preclude hoisting.
– Scott Marcus
Nov 11 at 22:18
add a comment |
up vote
-1
down vote
up vote
-1
down vote
function outer() {
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
This is surprising to me, given that foo has block scope, and I am
within the enclosing block when executing the inner function.
Because of hoisting, the declaration for foo
is hoisted to the top of the block, but not the initialization of the variable. It executes as if you had written:
function outer() {
let foo; // declaration only, so still undefined value
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
foo = 'bar'; // Not initialized until after you call inner!
};
Simply move the initialization up in the block and it will work:
function outer() {
let foo = 'bar'; // declared and initialized
function inner() {
if (foo) { ... } // works as expected
}
inner();
};
function outer() {
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
This is surprising to me, given that foo has block scope, and I am
within the enclosing block when executing the inner function.
Because of hoisting, the declaration for foo
is hoisted to the top of the block, but not the initialization of the variable. It executes as if you had written:
function outer() {
let foo; // declaration only, so still undefined value
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
foo = 'bar'; // Not initialized until after you call inner!
};
Simply move the initialization up in the block and it will work:
function outer() {
let foo = 'bar'; // declared and initialized
function inner() {
if (foo) { ... } // works as expected
}
inner();
};
answered Nov 11 at 21:32
Scott Marcus
38.3k51936
38.3k51936
2
The declaration offoo
is apparently not logically moved to the top of the outer() function (because if it was,typeof foo
would return 'undefined' without an exception). This would be the behavior if I had usedvar
to definefoo
, but in the case oflet
(andconst
) it is apparently not so. e.g., stackoverflow.com/a/31222689/925478
– Myk Willis
Nov 11 at 21:42
And as for moving the initialization to the top of outer(), that is unfortunately not possible in the actual production code I have, which initializesfoo
based on a function call that is only performed after a bunch of preprocessing work. e.g., we don't know the value to which it should be initialized until far down in the function.
– Myk Willis
Nov 11 at 21:43
2
"It executes as if you had written" - This is incorrect. One will give an error, the other will not.
– Spencer Wieczorek
Nov 11 at 21:43
@MykWillis The declaration of foo is moved to the top of the block (hoisted) as referenced by your link as well as here. It's just thatlet
scoped variables have a "temporal dead zone" which prevents access to them prior to initialization.
– Scott Marcus
Nov 11 at 22:17
@SpencerWieczorek I meant it processes logically as if it was written the way I showed. In other words, it's hoisted. The fact that it throws an error doesn't preclude hoisting.
– Scott Marcus
Nov 11 at 22:18
add a comment |
2
The declaration offoo
is apparently not logically moved to the top of the outer() function (because if it was,typeof foo
would return 'undefined' without an exception). This would be the behavior if I had usedvar
to definefoo
, but in the case oflet
(andconst
) it is apparently not so. e.g., stackoverflow.com/a/31222689/925478
– Myk Willis
Nov 11 at 21:42
And as for moving the initialization to the top of outer(), that is unfortunately not possible in the actual production code I have, which initializesfoo
based on a function call that is only performed after a bunch of preprocessing work. e.g., we don't know the value to which it should be initialized until far down in the function.
– Myk Willis
Nov 11 at 21:43
2
"It executes as if you had written" - This is incorrect. One will give an error, the other will not.
– Spencer Wieczorek
Nov 11 at 21:43
@MykWillis The declaration of foo is moved to the top of the block (hoisted) as referenced by your link as well as here. It's just thatlet
scoped variables have a "temporal dead zone" which prevents access to them prior to initialization.
– Scott Marcus
Nov 11 at 22:17
@SpencerWieczorek I meant it processes logically as if it was written the way I showed. In other words, it's hoisted. The fact that it throws an error doesn't preclude hoisting.
– Scott Marcus
Nov 11 at 22:18
2
2
The declaration of
foo
is apparently not logically moved to the top of the outer() function (because if it was, typeof foo
would return 'undefined' without an exception). This would be the behavior if I had used var
to define foo
, but in the case of let
(and const
) it is apparently not so. e.g., stackoverflow.com/a/31222689/925478– Myk Willis
Nov 11 at 21:42
The declaration of
foo
is apparently not logically moved to the top of the outer() function (because if it was, typeof foo
would return 'undefined' without an exception). This would be the behavior if I had used var
to define foo
, but in the case of let
(and const
) it is apparently not so. e.g., stackoverflow.com/a/31222689/925478– Myk Willis
Nov 11 at 21:42
And as for moving the initialization to the top of outer(), that is unfortunately not possible in the actual production code I have, which initializes
foo
based on a function call that is only performed after a bunch of preprocessing work. e.g., we don't know the value to which it should be initialized until far down in the function.– Myk Willis
Nov 11 at 21:43
And as for moving the initialization to the top of outer(), that is unfortunately not possible in the actual production code I have, which initializes
foo
based on a function call that is only performed after a bunch of preprocessing work. e.g., we don't know the value to which it should be initialized until far down in the function.– Myk Willis
Nov 11 at 21:43
2
2
"It executes as if you had written" - This is incorrect. One will give an error, the other will not.
– Spencer Wieczorek
Nov 11 at 21:43
"It executes as if you had written" - This is incorrect. One will give an error, the other will not.
– Spencer Wieczorek
Nov 11 at 21:43
@MykWillis The declaration of foo is moved to the top of the block (hoisted) as referenced by your link as well as here. It's just that
let
scoped variables have a "temporal dead zone" which prevents access to them prior to initialization.– Scott Marcus
Nov 11 at 22:17
@MykWillis The declaration of foo is moved to the top of the block (hoisted) as referenced by your link as well as here. It's just that
let
scoped variables have a "temporal dead zone" which prevents access to them prior to initialization.– Scott Marcus
Nov 11 at 22:17
@SpencerWieczorek I meant it processes logically as if it was written the way I showed. In other words, it's hoisted. The fact that it throws an error doesn't preclude hoisting.
– Scott Marcus
Nov 11 at 22:18
@SpencerWieczorek I meant it processes logically as if it was written the way I showed. In other words, it's hoisted. The fact that it throws an error doesn't preclude hoisting.
– Scott Marcus
Nov 11 at 22:18
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53253082%2ftest-whether-variable-declared-yet-in-containing-scope%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Use this keyword => this.foo to make sure the variable declared or not.
– Nattamai Jawaharlal Manikandan
Nov 11 at 20:51
@NattamaiJawaharlalManikandan The property
this.foo
would be something completely distinct from the variable defined in this scope.– Myk Willis
Nov 11 at 21:50
"Is there any safe way to test whether a block-scoped variable has yet reached its initialisation?" - no, not really, except for
try
/catch
. But what would you need this for? Either you will want to use the variable or you don't. If it throws an error because you try to access it before it is initialised, don't work around it but just fix your mistake - callinginner()
in the last line.– Bergi
Nov 11 at 22:29
@Bergi The reason this is needed in "real" code is because inner() is an error handling function, and outer() is a request processing function. In inner() (the error handling function), I want to gather all of the context information collected by outer() so that I can write it to an error response.
foo
, in the real world, is a variable that is the result of parsing input values to outer(), but there are conditions where we encounter an error beforefoo
is initialized. Thus, inner() may have to be called before foo is initialized.– Myk Willis
Nov 13 at 6:40
@MykWillis Can you make an example of how you are detecting the error? Is it a
try
-catch
? I'm unclear what the exact scope ofinner
andfoo
would be, wouldn't they be nested in different blocks? Also for what you described, I would recommend to simply usevar
(or put thelet
declarations at the top of your scope).– Bergi
Nov 13 at 8:39