你所知道的JS变量作用域

变量的作用域,指的是变量在脚本代码中的可读、可写的有效范围,也就是脚本代码中可以使用这个变量的区域。在ES6之前,变量的作用域主要分为全局作用域、局部作用域(也称函数作用域)两种;在ES6及其之后,变量的作用域主要分为全局作用域、局部作用域、块级作用域这3种。相应作用域变量分别称为全局变量、局部变量、块级变量。全局变量声明在所有函数之外;局部变量是在函数体内声明的变量或者是函数的命名参数;块级变量是在块中声明的变量,只在块中有效。

变量的作用域跟声明方式有密切的关系。使用var声明的变量的作用域有全局作用域和局部作用域,没有块级作用域;使用let和const声明的变量有全局作用域、局部作用域和块级作用域。

注:严格意义的全局变量都属于Window对象的属性,但let和const声明的变量并不属于Windows对象,所以它们并不是严格意义上的全局变量,在此仅仅从它们的作用域这个角度来说它们是全局变量的。

由于var支持变量提升,所以var变量的全局作用域是对整个页面的脚本代码有效;而let和const不支持变量提升,所以let和const变量的全局作用域指的是从声明语句开始到整个页面的脚本代码结束之间的整个区域,而声明语句之前的区域是没有效的。同样,因为var支持变量提升,而let和const不支持变量提升,所以使用var声明的局部变量是在整个函数有效,而使用let和const声明的局部变量从声明语句开始到函数结束之间的区域有效。需要注意的是,如果局部变量和全局变量同名,则在函数作用域中,局部变量会覆盖全局变量,即在函数体中起作用的是局部变量;在函数体外,全局变量起作用,局部变量无效,此时引用局部变量将出现语法错误。在块开始到块级变量声明语句之间区域为暂时性死区,在这个区域,块级变量没有效。

另外,在非严格运行模式中,变量可以不需要声明,这些没有声明的变量,不管在哪里使用都属于全局变量。通常不建议变量不声明而直接使用,因为这样有可能会产生一些不易发现的错误。

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
<script>
   var v1 = "JavaScript"; // 全局变量
   let v2 = "JScript"; // 全局变量
   let v3 = "Script"; // 全局变量
   
   scopeTest(); // 调用函数
   function scopeTest(){
       var lv = "aaa";//局部变量
        var v1 = "bbb";//局部变量
        let v2 = "ccc";//局部变量
        if(true){
            let lv = "123";
            console.log("块级输出的lv= " + lv); // 123
        }
        
        console.log("函数体内输出的lv = " +lv); //aaa
        console.log("函数体内输出的v1 = " +v1); //bbb
        console.log("函数体内输出的v2 = " +v2); //ccc
        console.log("函数体内输出的v3 = " +v3); //Script
        console.log("函数体内输出的v4 = " +v4); // undefined, v4为全局变量,赋值在后面,var存在变量提升,因而值为undefined
   }
   var v4 = "VB"; //全局变量
   console.log("函数体外输出的lv = " +lv); // 报ReferenceError错误
   console.log("函数体内输出的v1 = " +v1); //JavaScript
   console.log("函数体内输出的v2 = " +v2); //JScript
   console.log("函数体内输出的v3 = " +v3); //Script
   console.log("函数体内输出的v4 = " +v4); // VB
</script>
</body>
</html>


上述脚本代码分别声明了4个全局变量、3个局部变量和1个块级变量。在scopeTest函数体外,变量v1、v2、v3和v4为全局变量;在scopeTest函数体内,lv、v2是全局变量;在if判断块中,lv是块级变量。我们看到,局部变量v1和v2与全局变量v1和v2同名,在scopeTest函数体内,局部变量v1和v2有效,因而在函数体这2个变量的输出结果分别为bbb和ccc;在函数体外全局变量v1和v2有效,因而在函数体外,这2个变量的输出结果分别为JavaScript和JScript。另外,块级变量lv和局部变量lv同名,在if判断块中,块级变量lv有效,因而在块中输出的结果为123,而在块外,局部变量lv有效,lv变量的输出结果为aaa。另外,全局变量v3和v4在函数体中没有被覆盖,因而输出的是全局变量的值,所以v3在函数体外和体内输出结果都是Script,而v4变量的赋值在函数调用的后面,因而在函数体中的v4输出结果为undefined,而在函数体外的输出是在声明之后,所以结果为VB。lv是局部变量,因而在函数体外访问会报ReferenceError错误。

总结:块级变量在块内覆盖局部变量,局部变量在函数体内覆盖全局变量,没有被覆盖的全局变量在函数体内、外都有效。

        欢迎关注我的公众号前端历劫之路
        回复关键词电子书,即可获取12本前端热门电子书。
        回复关键词红宝书第4版,即可获取最新《JavaScript高级程序设计》(第四版)电子书。
        关注公众号后,点击下方菜单即可加我微信,我拉拢了很多IT大佬,创建了一个技术交流、文章分享群,期待你的加入。


作者:Vam的金豆之路

主要领域:前端开发

我的微信:maomin9761

微信公众号:前端历劫之路