参考链接阮一峰:ES6 let和const 光看文章没用。要输出,探讨。所以为了加强对let const的理解,记录下自己的学习过程
let和var的区别
let的基本用法应该都会,那么来看看let 和 之前var声明的变量之间有什么区别呢?
let不存在变量提升
先来看一个例子
console.log(a);
var a = 3
执行这段代码,会输出undefined。但是要彻底理解这段代码,是有点东西要讲的。那么我将从 执行上下文的角度,从变量对象的创建开始讲。 首先 将 globalContext压入 Execution Context Stack,进入执行上下文,先初始化variable Object,再 函数声明,变量声明(变量提升)。其中变量声明是不会覆盖同名的函数声明。 初始化作用域链,指定this为window,最后再开始执行全局代码。 所以在进入执行上下文的时候,就已经存在变量提升了,此时 a 已经声明,但是没有进行赋值为 3。 执行代码,打印 a 的值,就输出 undefined。 ps:其实这段代码也不是很复杂,如果给这段代码套上一个 函数作用域,那么就可以彻底串联起 词法作用域、 执行上下文栈、变量对象、作用域链 和 this的指向,这些基本的js问题了。这里不再展开。
所以var是存在 变量提升的。但是如果把这段代码改为
console.log(a);
let a = 3
这样的话,运行的时候就会报错,因为let是不存在变量提升的。 又会牵扯出一个新的问题,这时候对 a 进行报错,是因为存在 暂时性死区,并且let声明的变量是不放在全局作用域中的,而是放在脚本的作用域中。可以通过window.a 进行打印,打印的时候输出undefined。
同时,写的同时又想到了另一个问题。也是因为最近在学习this的指向的关系。 我把代码贴一下
let a = 1
function ap(){
console.log(this.a);
}
ap()
这会输出啥? 第一反应是输出 a。但是打印输出的是undefined。为什么呢?理一下逻辑。
在 进入执行上下文的时候,给 ap这个函数的scope绑定了 块级作用域 和 全局作用域,其中,a由于let定义,所以实在Script作用域中,ap是函数声明,所以是在window上。那么运行ap()函数,函数的this是window,而window上是没有a的,那么返回undefined
可以看一下chrome调试的截图
let不允许重复声明
直接上代码
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
ES6同时还带来了块级作用域的概念
块级作用域
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
f(); // undefined
分析这段代码,第一反应是 if(false) 这个循环进不去。但是由于 if里面的tmp是由 var定义的,不会单独分开一个作用域,所以tmp这个变量存在变量提升,在打印的时候就 输出变量提升的 undefined了。
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
改用ES6的块级作用域,
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
函数在块级作用域中进行声明
ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。
const命令
const声明一个只读的常量。一旦声明,常量的值就不能改变。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
const foo;
// SyntaxError: Missing initializer in const declaration
上面代码表示,对于const来说,只声明不赋值,就会报错。
const的作用域与let命令相同:只在声明所在的块级作用域内有效。
if (true) {
const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined
const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
if (true) {
console.log(MAX); // ReferenceError
const MAX = 5;
}
上面代码在常量MAX声明之前就调用,结果报错。
const声明的常量,也与let一样不可重复声明。
var message = "Hello!";
let age = 25;
// 以下两行都会报错
const message = "Goodbye!";
const age = 30;
本质
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
ES6 为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1
let b = 1;
window.b // undefined