程序开发 ·

JS变量let,var,const的区别

在ES6没出来之前,js中所有的变量都是用var来做声明的,ES6又添加了let,const用来声明变量。那么let,var,const声明的变量有什么区别呢?在这里我就对他们做一个总结。首先我们先引入一段代码如下:

let test_1 = 1;
var test_2 = 2;
const test_3 = 3;
function add(n1,n2,n3){
    console.log('n1:'+n1);//n1=1
    console.log('n2:'+n2);//n2=2
    console.log('n3:'+n3);//n3=3
    n1 += 1;
    n2 += 1;
    n3 += 1;
    console.log('n1:'+n1);//n1=2
    console.log('n2:'+n2);//n2=3
    console.log('n3:'+n3);//n3=4
}
add(test_1,test_2,test_3);
console.log('test_1:'+test_1);//test_1 =1
console.log('test_2:'+test_2);//test_2 =2
console.log('test_3:'+test_3);//test_3 =3

在上面代码中可以看到,在函数add中我们传入了分别用了let,var,const声明的变量,并且都+1了,但是输出的结果还是声明时候的值。这就说明了在add函数传入只是变量的值,而不是变量的引用,因此函数外部的变量值是没有改变的。那么到底有何不一样呢?先观察var:

console.log('test_1:'+test_1);//undefined
var test_1 = 2;

我们认为上面的代码肯定是不能运行的,但实际上却没有问题只是test_1 undefined。至于出现这情况就因为var声明的变量会在执行的时候提升其声明,这种叫做变量声明提升。也就是说var声明的变量会在代码执行的时候提升到函数的顶部。以上代码类似于

var test_1;
console.log('test_1:'+test_1);//undefined
test_1 = 2;

test_1变量自动提升声明在执行的时候。再看下面一段代码。

function test(){
    var test_1 = 1;
}
console.log('test_1:'+test_1);//ReferenceError: test_1 is not defined

test_1在这里出现了异常,这说var声明的变量是有作用域的,在test函数中声明的test_1只能在test中使用。

var test_1 = 2;
function test(){
    var test_1 = 1;
    if(true){
        var test_1 = 5;
        console.log('test_1:'+test_1);//5
    }
    console.log('test_1:'+test_1);//5
}
test();
console.log('test_1:'+test_1);//2

在上面这段代码可以看到,在test函数中test_1先前的值被后面的给覆盖了。但是却没有影响函数外面的test_1值。这里可以看出函数外面test_1是全局变量,函数中的test_1是局部变量,当局部变量和全局变量是一样的时候,改变局部变量的值对全局变量没有任何影响。而且在局部中可以重复声明,如此我们下面就把var声明改变用let声明。

console.log('test_1:'+test_1);//ReferenceError: test_1 is not defined
let test_1 = 2;

出异常了,这说明用let声明的变量不会在执行的时候进行 变量提升声明。再看下一段代码:

function test(){
    let test_1 = 1;
    console.log('test_1:'+test_1);
}
console.log('test_1:'+test_1);//ReferenceError: test_1 is not defined

异常了,说明let和var一样都有作用域。再一步观察

let test_1 = 3;
function test1(){
    let test_1 = 1;
    let test_1 = 2;//SyntaxError: Identifier 'test_1' has already been declared
    console.log('test_1:'+test_1);
}
test1();

又异常了,注意上面代码是在test函数的第二行出现的异常而不是在第一行,这就说明在同一个作用域中不能重复用let声明同一个变量。下面这段代码可以看出let和var的一个重要的区别:

let test_1 = 3;
function test2(){
    let test_1 = 1;
    if(true){
        let test_1 = 2;
        console.log('test_1:'+test_1);//3
    }
    console.log('test_1:'+test_1);//1
}
test2();
console.log('test_1:'+test_1);//3

对比上面两段代码,发现let在test1不能重复定义,但是在test2中if分支却又可以重复定义。并且不会影响test2函数中test_1和外面test_1的值。这就说明let声明的变量只能在其声明的分支中起作用,即let的作用域是块级作用域,而var是函数级作用域。下面总结下let和var的区别

  1. 作用域只限于分支,即块级(函数中的局部变量)
  2. 声明的变量不能自动提升声明(必须先声明后使用)
  3. 在同一个作用域中不能重复

接下来我们就需要看const声明的变量,又有何不同呢?

console.log('test_1:'+test_1);//SyntaxError: Missing initializer in const declaration
const test_1;

以上代码运行发现const声明的变量也是无法自动提升声明的。但是这个异常是变量没有初始化。我们对它赋值看看:

console.log('test_1:'+test_1);//ReferenceError: test_1 is not defined
const test_1 = 1;

在赋值以后,它的异常和let是一样的,let不管赋值与否都会出现这个异常,而const没有赋值会出现变量没有初始化的异常。这我就好奇了?为啥会出现这样的情况的呢?查看资料后发现const声明是创建一个值得只读引用,所以不指定一个引用的值是必须的。请注意 只读的引用!!这说明用const声明的变量是否不能重新赋值呢?

const test_1 = 2;
console.log('test_1:'+test_1);
test_1 = 3;//TypeError: Assignment to constant variable.
console.log('test_1:'+test_1);

通过以上代码发现const声明的变量确实不能重新赋值。通过查找资料发现这么一句话:const声明创建一个值的只读引用。但这并不意味着它所持有的值是不可变的,只是变量标识符不能重新分配。例如,在引用内容是对象的情况下,这意味着可以改变对象的内容(例如,其参数)。对于这句话我是思考了很久才清楚,就是用const声明的变量名称不能重新分配,而它的值却是可以改变的。当你把test_1 = 3的时候,其实就是把test_1这个名称重新分配给3,所以这样就会出异常。如下

const test = {
    a : 2
}
test.a = 3;
console.log('test.a:'+test.a); // 3

可以看到test对象中的a的值是可以改变的,但是你再也不能使用test这个标识符了。再瞧下一段代码:

const test_1 = 3;
function test2(){
    const test_1 = 1;
    if(true){
        const test_1 = 2;
        console.log('test_1:'+test_1);//2
    }
    console.log('test_1:'+test_1);//1
}
test2();
console.log('test_1:'+test_1);//3

如上可以看出const声明的变量和let的作用域是一样的。这样我们就可以很清楚的知道const区别于var的特性了:

  1. 作用域是块级
  2. 在同一个作用域不能重复声明
  3. 使用前必须声明并且赋值
  4. 起标志符不能重新分配

如此我们对JS中var,let,const声明的变量,以及它们的区别进行了一个系统的总结,ES6中还有很多其他的特性,我们需要进行探索。有时候看书一下浏览下去,好像看懂了,其实是没懂,因为这些简单的东西你看起来很容易,但是也容易忘记。当我们边看边总结加上思考这样才能对知识的吸收达到最大化!!

参与评论