前端基础:块级作用域 console.log(a,window.a)

388 阅读3分钟

一道块级作用域的有关的题:

console.log(a, window.a)
var a = 0;
console.log(a, window.a);
if(true){
    console.log(a, window.a);
    a = 1;
    console.log(a, window.a);
    function a(){}
    console.log(a, window.a);
    a = 21;
    console.log(a, window.a);
}
console.log(a, window.a);

如上你知道输出是什么?

点击查看答案:

console.log(a,window.a) // undefined undefined
var a = 0;
console.log(a,window.a); // 0 0
if(true){
    console.log(a,window.a); // ƒ a(){} 0
    a = 1;
    console.log(a,window.a); // 1 0
    function a(){}
    console.log(a,window.a); // 1 1
    a = 21;
    console.log(a,window.a); // 21 1
}
console.log(a,window.a); // 1 1
 

是不是结果有些意料之外?

图片名称

我们一起研究研究,怎么会出现这垃圾情况。

先试试没有function a(){}的时候打印会是什么样子的

console.log(a, window.a); // undefined undefined
var a = 0;
console.log(a, window.a); // 0 0
if(true){
    a = 1;
    console.log(a, window.a); // 1 1
    // function a(){}
    a = 21;
    console.log(a, window.a); // 21 21
}
console.log(a, window.a); // 21 21

如上可看到,当没有函数的时候就是会一切正常

此时可猜测,是作用域中的function a(){}捣的乱,通过多方查找资料找到的合理的解释是:

var a = 0;
if(true){
    a = 1;
    console.log(a, window.a); // 1 0
    function a(){} // 
    a = 21;
    console.log(a, window.a); // 21 1
}

1.函数声明被提升,并绑定到内部块变量。

意思就是,if {} 中因为查询到有函数的声明,便会把函数提到当前{}的最前面,此时该函数是内部的变量,并没有赋值到全局。

验证一下:

var a = 0
if(true){
    被提升的 function a(){} // 被提升到内部块的最上面
    // 此时内部块中有a变量,全局中并没有
    console.log(a, window.a); // ƒ a(){},  0
    //  window.a 为 0 此时全局中的a不会被改变
    function a(){} // 真正的函数声明处 
}

2.到达函数声明,将块变量复制到外部变量中。

意思就是,到达真正的函数声明处 ,才会把当前被提升的a 赋值给外部变量(全局)

验证一下:

var a = 0
if(true){
    被提升的 function a(){} // 被提升到内部块的最上面
    console.log(a, window.a); // ƒ a(){}, 0
    function a(){} // 真正的函数声明处,执行到这步时会才将变量a挂载在全居中
    
    console.log(a, window.a); // ƒ a(){},  ƒ a(){}
    //  window.a 为 ƒ a(){} 此时全局中的a才会被改变
}

验证二下:

因为能多次创建同名的函数,所以多次声明a函数,验证是不是只要遇见a的函数声明就会将a 赋值给外部变量

var a = 0;
if(true){
    console.log(a, window.a); // ƒ a(){}, 0
    a = 1; // 此时a改变的是内部块中的函数的提升
    console.log(a, window.a); // 1 0
    function a(){} // 遇见a的函数声明,此时将a赋值给外部变量(此时a为1)
    console.log(a, window.a); // 1 1
    a = 21;  // 此时a改变的是内部块中的函数的提升
    console.log(a, window.a); // 21 1
    function a(){} // 遇见a的函数声明,此时将a赋值给外部变量(此时a为21)
    console.log(a, window.a); // 21 21
}

如上,可得出结论: 函数会被提到当前{}的最前面,此时该函数是内部的变量, 等到达真正的函数声明处 ,才会把当前被提升的a 赋值给外部变量

结合这句话在回过头看一下题,是不是就清晰了?

反过来想想,也因为js是弱类型语言,才会出现这种怪异的现象。

延展学习

在ES5的时候只有函数作用域和全局作用域,es6新增了块级作用域。

块级作用域 {}

块级作用域中的定义函数、var变量会被提升

// 情况一:
if(true){
    c = 1
    var b = 0  
    function a(){} 
}
console.log(a, window.a); // ƒ a(){} ƒ a(){}
console.log(b, window.b); // 0 0
console.log(c, window.c); // 1 1

// 情况二:
try{
    c = 1
    var b = 0  
    function a(){} 
}catch {} 
console.log(a, window.a); // ƒ a(){} ƒ a(){}
console.log(b, window.b); // 0 0
console.log(c, window.c); // 1 1

函数作用域 function () {}

1.函数作用域中的定义var变量、创建函数

function add() {
   c = 1 // 未定义,在函数被调用的时候会在全局创建c
   var b = 0 // 不会污染全局
   function a(){} // 不会污染全局
}
add()
// console.log(a, window.a); //  a is not defined
//console.log(b, window.b); //  b is not defined
console.log(c, window.c); // 1 1

其他

可通过手动debugger来查看数据的变化情况,就会发现数据的改变时机。

image.png