JavaScript中的作用域 | 七日打卡

1,126 阅读4分钟

作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。

一、 var变量及其作用域

作用范围:

函数作用域,存在变量提升,即实际解析顺序与编码位置无关(赋值前预解析)

var 变量的作用域无非就是两种:全局变量局部变量

全局作用域

定义: 在所有函数之外的变量,函数内部可以直接读取全局变量。

  var name = "outer";
      function fn(){
         console.log(name);
      }
      fn(); //outer

局部作用域

定义: 一般只在固定的代码片段内可访问到,而对于函数外部是无法访问的,最常见的例如函数内部

  function fn(){
         var name = "inner";
      }
      fn();
      console.log(name); // ReferenceError: name is not defined

注意

1. 不用var定义出来的是全局变量

函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

     function fn(){
         name = "inner";
      }
      fn();
      console.log(name); // inner

2. 变量提升

只要函数内定义了一个局部变量,函数在解析的时候都会将这个变量“提前声明”

   var name = "global";
      function fn(){
         console.log(name); // undefined
         var name = "local";
         console.log(name);// local
      }
      fn();

相当于:

 var name = "global";
      function fn(){
         var name; // 提前声明了局部变量
         console.log(name);// undefined
         name = "local";
         console.log(name);// local
      }
      fn();

3. 块语句不会创建一个新的作用域

块语句(大括号“{}”中间的语句),如 if 和 switch 条件语句或 for 和 while 循环语句,不像函数,它们不会创建一个新的作用域

if (true) {
    // 'if' 条件语句块不会创建一个新的作用域
    var name = 'Hammad'; // name 依然在全局作用域中
}
console.log(name); // logs 'Hammad'

整体再来测试下,你会了?

var outVariable = "我是最外层变量"; 
function outFun() { 
    var inVariable = "内层变量";
    variable = "未定义直接赋值的变量";
    function innerFun() { //内层函数
        console.log(inVariable);
    }
    innerFun();
}
console.log(outVariable); //我是最外层变量
console.log(variable);//variable is not defined
outFun(); //内层变量,要先执行这个函数,否则根本不知道里面是啥
console.log(variable);//未定义直接赋值的变量
console.log(inVariable); //inVariable is not defined
innerFun(); //innerFun is not defined

小结

  • 不用var定义出来的是全局变量;
  • 用var在全局范围内定义出来的也是全局变量;
  • 用var在局部范围内定义出来的是局部变量。

二、 let变量及其作用域

作用范围:

块级作用域,不存在变量提升,不允许重复声明。

块级作用域

定义: 块语句(大括号“{}”中间的语句),如 if 和 switch 条件语句或 for 和 while 循环语句都会创建一个新的作用域

function func(arr) {
    for (let i = 0; i < arr.length; i++) {
        // 这里可以访问i ...
    }
    // 这里访问不到i
}

注意

1.不存在变量提升

var命令会发生“变量提升”的现象,即变量可以在声明之前使用,值为undefined。这 种现象多少有些奇怪,按照一般的逻辑,变量应该在声明语句之后才可以使用,为了纠正这种现象,let命令改变了语法行为,他所声明的变量一定要在声明后使用,否则报错

function func() {
    // name先使用后声明,报语法错
    alert(name)
    let name;
}

2.不允许重复声明

let不允许在相同的作用域内重复声明同一个变量

// 两个let重复声明
let age = 24;
let age = 30;

console.log(age) //  执行时报语法错

3.暂时性死区

暂时性死区本质就是:只要进入作用域,所要使用的变量就已经存在了,但是不可以获取,必须等到声明变量的哪一行代码出现。

 var tmp = 123;
    if (true) {
        tmp = "abc"; //报错ReferenceError
        let tmp;
    }

三、 var和let的区别

示例

下面的代码中,变量i是var声明的,所以i是一个全局变量在全局范围内都有效,所以全局只有一个变量i,每一次循环i的值都会发生改变,被赋给数组a的函数内部的console.log(i)中的i指向全局的i,因此所有数组a的成员中的i指向的都是同一个i,导致运行时输出的是最后一轮的i值10

var a = [];
for (var i = 0; i < 10; i++) {
     a[i] = function () {
         console.log(i);
     }
}
a[6]();//10

当我们使用let再来观察一下,变量i是let声明,当前的i只在本轮循环中有效,所以每一次循环都是一个新的变量,于是最后输出6。

    var a = [];
    for (let i = 0; i < 10; i++) {
        console.log(i);
        a[i] = function () {
            console.log(i);
        }
    }
    a[6]();//6

四、总结

var声明变量:

  1. 有变量提升

  2. 没有块级作用域,是函数作用域

  3. 能重复声明

  4. 可以重新赋值

let声明变量:

  1. 没有变量提升

  2. 有块级作用域

  3. 不能重复声明

  4. 可以重新赋值