开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
一、var
众所周知,js是一门松散的语言,与JAVA等静态语言不同,js可以通过一个var来声明任意类型的变量。
var a = 1; // 声明数字
var b = 'string'; // 声明字符串
在js中,下面这种用法也是合法的。
console.log(a); // undefined
var a = 1;
变量在声明前就能被访问到,这就是变量提升。在这里js引擎会把var声明的变量提升到其作用域的最顶部,相当于下面的代码:
var a;
console.log(a);
a = 1;
用var声明的同名变量也可以重复声明,后面声明的变量的值会覆盖前一个的值。
var i = '我是第一个i';
var i = '我是第二个i';
console.log(i); // 我是第二个i
我们都知道在ES6出现之前,js并没有块级作用域一说,即除了在函数内,所有用var声明的变量都是全局变量,而函数内声明的变量在函数执行后就会被销毁,达到阅后即焚的效果。
(function () {
var b = 'str';
console.log(b) // str;
})()
console.log(b) // ReferenceError
在js中,不使用关键字也能声明变量,并且用这种方式声明的变量都是全局变量,但是不提倡这种用法(可以,但是没必要),因为这容易给后期的代码维护增加成本。想象一下,如果你维护的代码中有一段代码使用的变量是在八百里开外的函数中声明的变量,是不是瞬间怒气值max。
a = 1; // 这种声明方式也是可以的
console.log(a); // a
于是乎就引申出了一道很经典的面试题:
(function () {
var b = c = 'str';
})()
console.log(c); // str
console.log(b); // ReferenceError
// 上面的代码相当于
(function () {
var b = 'str';
c = 'str'
})()
console.log(c); // str
console.log(b); // ReferenceError
二、let
说完var,我们来说一下let和const。在ES6中新增了let和const两个关键字,它们和var的主要区别是:1、有块级作用域;2、没有变量提升;3、不能重复声明。
用let声明的变量只在它所在的代码块内有效(我们可以理解为在它所在的{}内有效),如果你尝试在变量的作用域之外使用它,就会报错。
{
let a = '我是一个局部变量'
console.log(a) // 我是一个局部变量
}
console.log(a) // ReferenceError
用let声明的变量是没有变量提升的,它会锁定它所在的代码块,使它不会被外部所影响,我们称之为暂时性死区。在变量声明之前使用它会报错,即使同名的变量在外部曾被声明过。
console.log(a) // ReferenceError
let a = '我真的不能变量提升'
// 像下面这样也会报错
let a = '我是外部的变量';
{
console.log(a); // ReferenceError
let a = '我是里面的变量';
}
let不能重复声明在其作用域内已经被声明过的函数。
var k = '我是var声明的';
let k = '我是let声明的'; // SyntaxError
// 下面这种用法是可以的
var k = '我是var声明的';
{
let k = '我是let声明的';
}
三、const
const和let声明的许多特征是一样的,它们最大的区别就是:const声明的是常量,一旦声明了就不可更改。
const a = '我是const声明的';
a = '改一下我的值'; // TypeError
这个不可更改本质上是指变量的指向的那个内存地址不能更改,对于Number、String等基本数据类型来说,它们就保存在了变量指向的内存地址中,但是对Object等引用数据类型而言,变量指向的内存地址只是它们的指针,const只能保证内存地址不变,但是不能保证内存地址所指向的数据结构有没有改变。
const obj = {
a: '我是a的初始值'
}
obj.a = '我是a变化后的值';
console.log(obj) // {a: '我是a变化后的值'}
以上就是我总结的var、let和const的特征及用法,如有不对或者表达不清的地方,请多多指正。