【ES6系列】 var、let、const 三个关键字

ES5 中,我们使用关键字 var 声明变量。但是,随着这门语言的发展,以及业务需求的不断改进,var 这个老家伙似乎存在不少问题,比如:变量提升,没有块级作用域等等。所以,在 ES6 中新增了 letconst 两个关键字来解决这些问题。

变量提升

var 关键字带来的问题之一就是变量提升。

在讨论变量提升之前,先考虑一个问题:我们写的 js 代码或者说脚本如何被解释器解释的呢?

在此处只讨论代码如何执行,对于具体的解释过程参考后面的文章 (待更)。

首先,一个 js 脚本文件肯定不是 “无脑” 的从上到下执行的。解释器会先把使用 var关键字声明的变量提到最前面,注意,此时并不会进行赋值操作,只是声明变量(初始化值为 undefined)。这种现象就被称为变量提升。这种现象带来的是一种不安全的行为:

a = 2;
var a;
console.log(a);//2

上面这段代码的执行顺序是:

  1. 声明变量 a
  2. 将 2 赋值给变量 a
  3. 输出变量 a 的值

这很奇怪吧,也不是我们想要的效果,我们往往更期望的是一个变量必须在声明以后才能进行赋值操作,否则我们期望得到的是一个错误对象。

关键字 let 的出现就解决了变量提升的问题,使用 let 定义的变量只有在初始化以后才能对一个变量进行操作:

//正确
let a = 2;
console.log(a);

//错误
a = 2;
let a;
console.log(a);//报错,a is not defined

或许你会对上面的代码有疑问:如果在全局作用域中直接使用一个未使用任何关键字声明的变量(比如:a = 2),那么这个变量不会被定义为全局对象 window 的属性吗?

答案就是接下来要讲的全局属性和临时死区。

全局属性和临时死区

在全局作用域中,直接使用一个未通过任何关键字声明的变量,那么这个变量就会被定义为全局对象的属性:

a = 2;
console.log(window.a === a);//true

在全局作用域中,使用关键字 var 声明的变量也会被定义为全局对象的属性:

var a = 2;
console.log(window.a === a);//ture

到了这里,可以下这么一个定论:在全局作用域中,使用 var 关键字或者不使用任何关键字声明的变量,都会被定义在全局对象上作为属性。

但是,对于前面提出的问题还没有回答:

//错误
a = 2;
let a;
console.log(a);//报错,a is not defined

你会认为先执行 a = 2 ,然后此时不就在 window 上添加了一个属性 a 吗?所以打印应该输出 2,为什么会报错呢?

这里就涉及到另外一个概念:临时死区

在一个作用域中(无论是全局作用域、函数作用域甚至是块级作用域) ,凡是通过 letconst 关键字声明的变量,那么它就是这个作用域的唯一,不能在它们声明之前的任何地方使用,所以,在一个变量声明(通过 letconst)之前的地方就称为这个变量的临时死区。 比如上面的例子 a = 2 就是一个临时死区。在临时死区中,访问变量就会抛出异常。

除了上面的情况以外,函数作用域里面也有临时死区:

var a = 2;
function foo(){
    a = 3;
    let a = 2;
}
foo();//报错,a is not defined

报错的原因也是一样的,都是因为在临时死区中访问了变量。

所以,letconst 两个关键字虽然没有变量提升的问题,但是出现了一个临时死区需要我们注意。我们应该在声明变量以后再进行操作,这才是一个好的习惯。

块级作用域

ES5 中,并没有块级作用域这个说法,比如,条件语句,循环语句等。它们不是一个完整的作用域。没有块级作用域会导致很多不可思议的问题,例如:

for(var i = 0;i<10;i++){
    console.log(i);
}
console.log(i);//10

上面的这个循环只是为了打印数字 0-9 ,但是奇怪的是,作为循环标记的变量 i 竟然在循环以外的地方还能访问,这就造成了作用域污染的问题了,这也不是我们期望的,因为这个循环标记只用在循环体里面,出了循环以外这个标记将毫无意义。所以,在 ES6 中,就引入了块级作用域的概念,条件语句,循环语句,凡是使用大括号 {} 包裹着的代码,都是一个块级作用域,在块级作用域中,如果使用 letconst 定义的变量,那么在这个作用域以外(下级的作用域除外)的地方就无法访问这些变量。

//块级作用域
{
    let a = 2;
}
console.log(a);//报错,a is not defined

总结:凡是通过 letconst 定义的变量都存在块级作用域。

const

ES5 中,如果需要定义一个常量,那么还是使用关键字 var ,然后从编程习惯上将这个变量的所有字母都用大写来表示,让人从直观上看这是一个常量,但是本质并不是一个常量,我们仍然可以给它赋值。所以,这不是定义常量的方法。从而,为了解决这个问题,ES6 引入了关键字 const

在我的理解里面,const 关键字只是将一个变量在栈内存中储存的信息锁定了,比如:

const a = 2;//变量a在栈内存中保存了一个值为2,这个值不能被修改
const obj = {};//变量obj在栈内存中保存了一个对象(一个16进制的引入地址值),这个值不能改变(并不代表对象中的内容不能改变)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值