JS中变量(var、let、const)的声明提升和作用域梳理

您所在的位置:网站首页 js中var的作用域 JS中变量(var、let、const)的声明提升和作用域梳理

JS中变量(var、let、const)的声明提升和作用域梳理

2023-11-20 05:59| 来源: 网络整理| 查看: 265

 在JS中有三个声明变量的关键字

var:最常用的,可声明全局变量,也可声明局部变量;let:块级作用域, 声明的变量只能在其声明的块或子块中可用;const:声明常量,声明变量的时候必需初始化,且之后不可改变。 

下面结合代码,具体说明:

1. 变量声明和赋值

var可以在同一条语句声明并赋值多个变量,以逗号分隔。但,同一条语句声明的多个变量并不会被赋予同一个值。

var a=1,b=2,c='mmm'; // 声明并赋值a、b、c var a,b,c=1; // a和b为undefined,c为1 (a和b只被声明了,没有被赋值)

let的用法同var一样

let a=1,b=2,c='mmm'; // 声明并赋值a、b、c let a,b,c=1; // a和b为undefined,c为1 (a和b只被声明了,没有被赋值)

const声明一个常量,声明和赋值是一起的。只声明不赋值,报错。

const a=1,b=2,c='mmm'; // 声明并赋值a、b、c const a; // 报错: Missing initializer in const declaration 2. 变量重新声明

 var 可以重新声明变量。且重新声明变量时,该变量原有的值不会丢失。重新声明可以理解为重新赋值,若是重复声明是没有赋值,则不影响之前的变量。

var a; var a=1; console.log(a); // 1 var b=1; var b; console.log(b); // 1 // 重新声明变量,该变量原有的值不会丢失 var c=1; var c=3; console.log(3); // 3 // 重新声明变量,覆盖该变量原有的值

变量重新声明

let不可以重新声明变量。

let a; let a=3; // 报错:Identifier 'a' has already been declared

但是,不在同一个作用块的时候,是可以的。此时不是重新声明,而是在另一个作用块声明了一个同名变量。(后面详述)

function test(){ let a; if(true){ let a=3; console.log(a); } console.log('外边的:' + a); } test();// 依次输出:3 外边的:undefined

const也不可以重新声明变量。若是重新声明也会同let一样直接报错。

const a=1; const a=3; // 报错:Identifier 'a' has already been declared

这时候有人会问,那像let一样在不同的作用块时,可以吗?答案是可以,原理同let一样,是在另一个作用块内声明了一个同名变量。

3. 变量声明提升

只有var有变量声明提升的概念,let和const是没有的。

那什么是变量声明提升呢?就是在未声明变量的时候,先使用了,此时就会默认将该变量声明的功能提升到使用之前,但是不会提升该变量的赋值。

console.log(a); // 输出:undefined 先使用变量,后声明赋值-->变量声明会提升,但赋值不会提升 var a=1; console.log(a); // 输出:1

但若是,代码里前后都没有声明变量,直接打印,会报错

console.log(b); // 会报错:b is not defined. 因为这里只是使用变量,前后都没有声明。

let必须先声明后使用,否则会报错。

console.log(a); // 报错:Cannot access 'a' before initialization let a=3;

const在前面已经提到,声明和赋值必须是一起的,否则会报错。

4. 变量作用域

变量作用域简单来说就是变量的作用区域。在js中,有局部变量和全局变量之分。

显式声明变量:用关键字声明的,如:var a=1;   ----> 局部变量:在函数内或块内声明的变量隐式声明变量:不用关键字,如:b=2;                  ---->全局变量:无论在哪个位置声明

function test(){ var a=1; b=2; console.log(a); console.log(b); } test(); // 输出:1 2 console.log(a); // 报错:a is not defined. console.log(b); // 2

在上面的示例中,test函数中显示声明了变量a、隐式声明了变量b,即:a是局部变量,b是全局变量。所以在test函数外部使用a的时候会直接报错,使用b是没问题的。

在调用函数之前,声明并赋值变量,会影响函数内部吗?

带着疑问,依据示例来进行讨论:

函数外部和内部均声明赋值了一个变量a,见示例:

function test(){ var a=1; console.log(a); } var a=2; console.log(a); // 2 test(); // 1 使用的是函数内部声明的变量a console.log(a); // 2 函数外定义的变量不受影响

由代码运行结果可判断出:函数内部使用的变量a是自己内部声明的,且不影响函数外部的同名变量。

有人说,先声明了一个变量a,函数中又声明一个变量a,这不是“变量重新声明”吗?不是的,最有利的证据就是最后一行a的值依旧是2,若是变量重新声明,这里的值应该也变为1。

再举一个例子,在函数内部先使用,后声明赋值,见示例:

function test(){ console.log(a); var a=1; } var a=2; console.log(a); // 2 test(); // undefined 使用的是函数内部声明的变量a,函数内部将a变量声明提升了 console.log(a); // 2 函数外定义的变量不受影响

这个例子就很明确了,函数内部使用的是自己声明的变量。

但若是函数内部没有定义该变量,直接使用的话,会如何?见示例:

function test(){ console.log(a); // var a=1; } var a=2; console.log(a); // 2 test(); // 2 使用的是函数外部声明的变量a,函数内部没有声明a变量 console.log(a); // 2 函数外定义的变量不受影响

当函数中没有声明相关的变量时,才会使用函数外部的变量。

这里可以总结出一个原则:就近原则(自己有就用自己的,自己没有的时候去外部寻找)

上面是显示声明变量的结果,若是函数内部使用的是隐式声明呢?

function test(){ a=1; console.log(a); } var a=2; console.log(a); // 2 test(); // 1 console.log(a); // 1 函数中隐式声明的变量影响了函数外的变量

那么若是函数外部不声明a呢?

function test(){ a=1; console.log(a); } // var a=2; console.log(a); // 报错:a is not defined test(); // 1 console.log(a); // 1

函数上面使用a变量时,直接报错了。

所以,这块整体可以理解为:

隐式声明的变量:比如:a=1;

当已经声明过变量a,那么后续的 a=1 就是赋值或修改原来的a;当没有声明过变量a,那么后续的 a=1 就是隐式声明并赋值了一个全局变量,且隐式声明的变量,没有变量声明提升的概念。

上面所有的内容都是以var为准进行说明的,let的使用与var是类似的。

区别在于:var的作用块在整个函数内部,而let的作用块相对来说小一些,在某一个条件或循环语句中。

原理一样:就近原则(子块内有就用自己的,子块没有的就用父块的。子块可以修改父块的,但子块自己的变量只影响自己,不影响父块)

补充一个示例说明:

function test(){ let a=0; if(true){ let a=111; console.log(a) // 111 } if(true){ console.log(a) // 0 } if(true){ a++; console.log(a) // 1 } console.log('外边的:' + a) // 外边的:1 } test();

但若是将上面的let更改为var,就是不一样的结果,因为var不会细化到块,只到函数。

function test(){ var a=0; if(true){ var a=111; console.log(a) // 111 } if(true){ console.log(a) // 111 } if(true){ a++; console.log(a) // 112 } console.log('外边的:' + a) // 外边的:112 } test();

从“3. 变量声明提升”可以知道,let是没有变量声明提升概念的。那若是,在块内和块内都声明了一个变量,但是在块内使用在声明之前会如何呢?

function test(){ let a=0; if(true){ console.log(a) // 报错:Cannot access 'a' before initialization let a=111; } } test();

看明白了吧,只要自己有,就用自己的,报错也阻挡不了。

所以针对var和let可以统一总结为:

自己有就用自己的,自己没有就用父亲的;(只要自己有,即便报错,也不用父亲的)

var的作用域为整个函数,let的作用域为某个块。

const就不用额外说明了,声明的同时需要赋值,且后续不可修改。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3