变量的声明在每个编程语言里都会有,JavaScript的变量声明有var
、let
,常量的声明是const
。这几种声明方式的作用域有一定差别,以var
举例,以var
声明的变量会被隐式地创建为全局变量,并且还会产生变量提升的效果。
在讲解 var
、let
和 const
之前应了解什么是变量提升,这很重要。
先来看看这几行代码,判断是否能够正确打印不抛出异常
// 代码片段一
a = 1;
console.log(a); // 打印:1
// 代码片段二
var a = 1;
console.log(a) // 打印:1
// 代码片段三
console.log(a) // 打印:undefined
var a = 1;
// 代码片段四
console.log(a) // 抛出异常
// 代码片段五
var a = 1;
{
console.log(a) // 打印:1
}
// 代码片段六
console.log(a) // 打印:undefined
{
var a = 1;
console.log(a) // 打印:1
}
var
的方式进行了声明。var
进行声明的方式,结果当然能输出。undefined
,这印证了在代码片段一中所说的,将变量提前以var
的方式进行了声明。ReferenceError
异常。var
声明变量写在了块里,变量依旧可以在声明之前使用,存在变量提升的现象。再来看看其他声明方式的效果
// let 声明变量
console.log(a);
let a = 1;
console.log(a);
// const 声明变量
const a = 1;
console.log(a);
以这两种方式声明,在声明之前调用变量会抛出 ReferenceError
异常,只有在声明之后调用不会异常,可见这两种方式是不存在变量提示的。
总结
var
和 function
在作用域内 存在着变量提升。undefined
。function
的变量提升能够使函数在声明前能够被调用。let
、const
这两种声明变量的方式不会造成变量提升。var
声明一个变量,可同时将其初始化为一个值,它是一个全局作用域的声明方式,也被函数作用域限制。但因其全局作用,以及提前声明的特性,容易造成变量的全局污染,造成意想不到的后果。
1、声明一个变量
// 声明一个变量
var a;
2、声明并初始化一个变量
// 声明并初始化一个变量
var a = 1;
3、声明并初始化两个个变量
// 声明并初始化两个个变量
var a = 1, b = 2;
4、多个变量的初始化
// 多个变量的初始化
var a;
function f() {
var a = b = 1
}
f()
console.log(a) // 打印:undefined
console.log(b) // 打印:1
在函数里以var a = b = 值
的方式将变量a
和b
同时赋值为了1,这种操作等用于var a = 1; b = 1
。但调用函数后,函数外打印打印这两个值时能正确执行 且 值不相等。
b
变量只在函数中出现过,根据局部能调用全局作用域,全局不能调用局部作用域的特点,b
应该会抛出未定义的异常才对,但实际上并没有这样。这是因为在函数内部初始化变量b
的时候,并未指定声明符,那么JavaScript将变量提前以var
的方式进行了声明,而这个提升是全局的。a != b
,其实很简单,在程序最开始就声明了变量var a
,而执行函数时并没有将a
的值修改为1,这是因为被函数作用域所限制,1这个值只在函数中生效。5、意想不到的全局污染
for (var i = 1; i <= 10; i++) {
// 延时一秒后执行
setTimeout(function () {
console.log(i); // 输出十次11
}, 1000)
}
var
变量在使用循环时搭配定时器,会产生意想不到的效果,程序的目的是在一秒之后分别输出1-10的值,结果却是输出十次11。将 var
改成 let
则恢复正常。
var x = 1;
function func1() {
var y = 2;
function func2() {
x = 4;
y = 5
z = 6;
}
func2()
console.log(x, y, z) // 打印:4, 5, 6
}
func1()
console.log(x) // 打印:4
上述代码,x
、y
、z
打印的值各是多少呢?将x
、y
、z
分开来看。
x
来说它是写在所有函数之前的全局变量,对x
的修改是全局的,x
的最后一次修改是在func2
函数里,那么x
的值到最后应该为4。y
来说,是在func1
函数中声明的,那么其作用域只在func1
函数中生效,y
的最后一次修改是在func2
函数里,那么y
的值到最后应该为5。z
的值只出现过一次赋值,在此之前并没有任何的变量声明,在func2
函数中以z = 5
表达式赋值为5,其写法相同于var z = 5
,那么z
的值到最后应该为6。6、可重复声明
// 重复声明
var a = 1;
var a = 2;
console.log(a) // 打印最后赋的值:2
// 循环重复声明
var x;
for (var i=1; i <= 5; i++) {
var x = i;
}
console.log(x) // 打印最后赋的值:5
使用var声明变量,是可以重复声明的,这有个好处就是写起来比较灵活,不会抛出重复声明的异常。
let 是块级作用域的本地变量。与 var
声明不同的是,let不允许重复声明同一个变量,将会抛出 SyntaxError
异常,但可以修改变量的值。
// 块级作用域
{
let a = 1;
console.log(a) // 打印:1
a = 2;
// let a = 3; // 抛出 SyntaxError
}
console.log(a) // 抛出异常 ReferenceError
以 let
声明的变量不存在变量提升,在变量初始化前访问该变量会抛出 ReferenceError
异常,自块开始到在初始化之前属于暂存死区。
// 不存在变量提升
console.log(a) // 抛出 ReferenceError
let a = 1;
{
console.log(b) // 抛出 ReferenceError
let b = 2;
}
使用 let
声明的变量能够解决 三、var 变量声明 ⇨ 5、意想不到的全局污染 中所提到的全局污染。
此时程序能正确输出我们所期望的值,1-10之间的数。
for (let i = 1; i <= 10; i++) {
// 延时一秒后执行
setTimeout(function () {
console.log(i); // 输出1-10
}, 1000)
}
此时每个变量在各自的作用域里各司其职,河水不犯井水,对于我们编写代码来说不会出现一些意外的值。
let x = 1;
function func1() {
let y = 2;
function func2() {
let x = 4;
let y = 5
let z = 6;
console.log(x, y, z) // 打印:4, 5, 6
}
func2()
console.log(x, y) // 打印:1 2
}
func1()
console.log(x) // 打印:1
const 是块级作用域的本地常量。与变量不同的是,常量不支持重复声明也不支持修改,若是存在修改行为则会抛出 TypeError
异常。
// 块级作用域
{
const BASE = "块级";
console.log(BASE) // 正常打印
// const BASE = "重复赋值" // 抛出 SyntaxError
// BASE = "修改"; // 抛出异常 TypeError
}
console.log(BASE) // 抛出异常 ReferenceError
常量名和其他声明方式一样,都可以大小写字母,但程序员之间会使用全大写进行命名,这是为了便于辨别常量类型,可以说是程序员之间的一种默契。
// 声明常量
const BASE = "常量名大写"
// 声明并初始化两个个变量
const BASE_NAME1 = 1, BASE_NAME2 = 2;
const
常量不存在变量提升,在常量初始化前访问该常量会抛出 ReferenceError
异常,自块开始到在初始化之前属于暂存死区。
// 不存在变量提升
console.log(a) // 抛出 ReferenceError
const a = 1;
{
console.log(b) // 抛出 ReferenceError
const b = 2;
}
对于性能来说,const
不允许重新赋值的,所以转换逻辑很简单,其潜在优化比较好。但是这种差异是细微的,几乎察觉不到,使用常量更多是注重代码风格,便于阅读。
声明 | 描述 | 作用域 | 是否存在变量提升 |
---|---|---|---|
var | 声明一个变量,可同时将其初始化为一个值 | 全局作用域 | ✅ |
let | 声明一个块级本地变量,可同时将其初始化为一个值 | [块级/局部]作用域 | ❎ |
const | 声明一个只读的命名常量 | [块级/局部]作用域 | ❎ |
const
进行声明将其变为常量。这样有利于代码的阅读,不用考虑这个变量会不会发生改变,还能提高程序的 潜在 执行效率。var
进行声明,因其全局破坏的影响,尽量使用 let
或 const
进行取缔。var
进行声明会有明确的弱警告提示。给笔者加个