personally, to explain this problem, you need to understand SC (scope chain)
, VO (variable object) / AO (active object)
.
the first problem of the subject: because there is a bs variable in the function domain, the bs variable in the global domain is overwritten, so the bs variable in the global domain is not output.
function bs(){
console.log(1)
}
function fn(){
console.log(bs)
}
fn()
// bs(){
// console.log(1)
// }
the second problem of the subject: VO, is initialized in the function field, so bs is underfined
(in this case all variables are underfined
), but the block-level AO, is initialized only when executed to the block-level area of if
, so bs is function
at the top of the block level.
function fn() {
console.log(bs)
if (true) {
console.log(bs)
function bs() {
console.log(2)
}
}
}
fn()
// undefined
// bs() {
// console.log(2)
// }
in addition, I just saw that strict mode is mentioned in the comments, and undeclared variables are not allowed in strict mode, that is, you cannot access VO in SC. So you are accessing global variables. An error will be reported when accessing local variables, but under the block-level area of if
, you can access AO, to achieve variable promotion.
(function(){
"use strict"
function fn(){
console.log(bs)
if (true) {
function bs(){
console.log(2)
}
}
}
fn()
})()
// Uncaught ReferenceError: bs is not defined
(function(){
"use strict"
function fn(){
if (true) {
console.log(bs)
function bs(){
console.log(2)
}
}
}
fn()
})()
// bs(){
// console.log(2)
// }
< hr >
2018-11-04 add
received questions in other answered comments, with regard to variable improvement, the core of the question is the promotion in the context of variable improvement.
that should first explain the problem of variable promotion in the context of the code:
first of all, JS has no block-level scope, secondly, the newly added block-level scope of ES6 is only for let and const (it feels like tinkering for three years), and then there is no write or var only has the default < script > scope and function scope. Only in this case, variables are promoted, that is, to the top of each of these two fields.
question 1: why do variables rise?
because JS is an interpretation-and-execute language and is OOP, it is very inefficient to modify the variable tree ( SC scope chain
) while executing, so JS initializes all code in each context domain before executing code, finds var naming and omits naming, function naming, and adds variables to the variable tree, meaning to tell the following execution statement. Do you have any variables you can use. At this point, accessing the variable at the top of the domain outputs underfined
. Also known as variable promotion.
Scope = AO&VO + [[Scope]]
where AO is always at the front end of Scope
.
answer the background of the variable promotion: the console reports an error is not function
instead of is not defined
indicating that the variable has indeed improved (ignoring the if
block and promoting it directly to the top of the function field).
question 2: the problem of variables with the same name as function names?
The
function is a first-class citizen of all languages, and JS is no exception, so if the function and the variable have the same name, accessing the variable with the same name at the top of the scope after promotion will output the function definition, that is, whether the variable is named first or the function is named first, it will be overwritten as the function definition.
console.log(a)
var a
function a () {}
// a () {}
console.log(a)
function a () {}
var a
// a () {}
as for the question of the subject, bs is not defined as a function, so refer to my answer: at this time, the variable is only VO, not AO,. It depends on the ECMAScript specification.
question 3: let and const and ES6 block-level scope?
let and const will form the temporary dead zone problem proposed by Mr. Ruan, which is actually shown as follows: when initializing the variable tree under the basic scope (< script > and function space), if you access the variable tree with the same name let or const variable definition, it will delete the named variable on the variable tree and prevent subsequent var and function definitions, and unwritten direct definition variables will be added to the variable tree (at this time the error will be defined).
var a
function b () {
console.log(a)
let a
}
b()
// Uncaught ReferenceError: a is not defined
the behavior of let and const to join the variable tree is when the statement is executed, and it is strongly related to the block structure of the current context (the variable is defined within the new first-level execution context), so the variable can be accessed later within the block, and once the execution location leaves the current block structure (the new first-level execution context), the variable on the variable tree is deleted. Does not affect the external variable definition ( SC
of the parent block).
var a = 1
{
let a = 2
console.log(a)
}
console.log(a)
// 2
// 1
question 4: what is the difference between the rules and behavior of function?
ES5 stipulates that functions are not allowed to be defined in block-level regions; ES6 stipulates that functions can be defined in block-level regions, but there is no variable promotion, similar to let (which can be understood as a new definition).
but! Point!
function naming definition. Browsers have not followed the rules since ES5 and can be defined at the block level, so it is mentioned in the appendix in ES6: for the sake of backward compatibility, forget about it if you don't follow it, just like var, as long as you are happy.
therefore, all of the above explanations are based on this situation.
< H2 > Correction < / H2 >
function f() { console.log('I am outside!'); }
(function () {
if (false) {
// f
function f() { console.log('I am inside!'); }
}
f();
}());
the actual running effect of the above code:
// ES6
function f() { console.log('I am outside!'); }
(function () {
var f = undefined;
if (false) {
function f() { console.log('I am inside!'); }
}
f();
}());
so the understanding of the landlord is correct. Please refer to @ rhinel for internal details.
< H2 > mistakes made in the original answer < / H2 >
in the original answer, I made the following mistakes:
The new block-level scope of - ES6 can only be built by let, const and curly braces, but not directly through curly braces. The example in the original answer is misplaced
.
- output 2 in the ES5 browser environment is the result of the browser's own implementation, which is designed to be compatible with the old code. According to the specification, function declarations should not be used in ES5 functions. Function declarations are allowed in ES6 functions, but they behave like let, and variables have been promoted
- neither ES5 nor ES6, should write function declarations in function scope. Writing function expressions is easier to understand.
< H2 > original answer < / H2 >
I see this question in the weekly selection, and I have different ideas about it.
if you are in ES5, the title of the landlord should print out 2
because the function declaration will be promoted to the top of the function-level scope, that is,
function bs(){
console.log(1);
}
function fn(){
function bs(){
console.log(2)
}
bs();
if(false){}
}
fn(); // 2
the reason why this question is undefined is that current browsers already support ES6,if (false) {} curly braces to form a block-level scope. The following modification ( the following example error, the key is that the function declaration in the function scope is a special case in the ES6 environment ) can illustrate that it is indeed a block-level scope problem:
// {}
function bs(){
console.log(1);
}
function fn(){
bs();
{
function bs(){
console.log(2)
}
}
}
fn(); // bs is not function
according to Ruan Yifeng ES6 tutorial ," ES6 stipulates that in block-level scope, function declaration statements behave like let, can't be referenced outside the block-level scope. " On the other hand, there is a temporary dead zone in let definition:
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
"in the above code, there is a global variable tmp, but let declares a local variable tmp, in the block-level scope, which causes the latter to bind to this block-level scope, so the assignment of tmp will report an error"
before let declares the variable.
therefore, this topic is the pit of ES6 block scope and let temporary dead zone (should be the pit of function declaration in ES6 environment)
The promotion of the
variable has scope. If the variable cannot be found in the current scope, it will only be found in the scope of the next level. In the example you gave, function fn () {
in the scope of the fn function
bs();
if(false){
function bs(){
console.log(2)
}
}
} is equivalent to = "function fn () {
var bs; / / when bs is underfined, of course it's not a function.
bs();
if(false){
bs= function (){
console.log(2)
}
}
}
first of all, although ES5 does not have a block-level scope, it has a function field, and there is no way to access the global within the function.