var与let、const
一、var声明的变量会挂载在window上,而let和const声明的变量不会:
var a = 100;
console.log(a,window.a); // 100 100
let b = 10;
console.log(b,window.b); // 10 undefined
const c = 1;
console.log(c,window.c); // 1 undefined
ES6规定如果块内存在let命令,那么这个块就会成为一个封闭的作用域,并要求const、let变量先声明才能使用,如果在声明之前就开始使用,它并不会引用外部的变量。
二、var声明变量存在变量提升,let和const不存在变量提升(ps:这里的提升是指初始化提升,后面会讲到)
console.log(a); // undefined ===> a已声明还没赋值,默认得到undefined值
var a = 100;
console.log(b); // 报错:b is not defined ===> 找不到b这个变量
let b = 10;
console.log(c); // 报错:c is not defined ===> 找不到c这个变量
const c = 10;
三、let和const声明形成块作用域
if(1){
var a = 100;
let b = 10;
}
console.log(a); // 100
console.log(b) // 报错:b is not defined ===> 找不到b这个变量
if(1){
var a = 100;
const c = 1;
}
console.log(a); // 100
console.log(c) // 报错:c is not defined ===> 找不到c这个变量
四、同一作用域下let和const不能声明同名变量(即不允许重复定义),而var可以
var a = 100;
console.log(a); // 100
var a = 10;
console.log(a); // 10
let a = 100;
let a = 10;
// 控制台报错:Identifier 'a' has already been declared ===> 标识符a已经被声明了。
五、暂存死区
拥有块级作用域的变量不能在被声明之前读或写。 虽然这些变量始终“存在”于它们的作用域里,但在直到声明它的代码之前的区域都属于 暂时性死区
var a = 100;
if(1){
a = 10;
//在当前块作用域中存在a使用let/const声明的情况下,给a赋值10时,只会在当前作用域找变量a,
// 而这时,还未到声明时候,所以控制台Error:a is not defined
let a = 1;
}
function foo() {
// okay to capture 'a'
return a;
}
// 不能在'a'被声明前调用'foo',此时的代码在暂时性死区里面,在后面调用就不会了
// 运行时应该抛出错误
foo();
let a;
六、const
/*
* 1、一旦声明必须赋值,不能使用null占位。
*
* 2、声明后不能再修改
*
* 3、如果声明的是复合类型数据,可以修改其属性
*
* */
const a = 100;
const list = [];
list[0] = 10;
console.log(list); // [10]
const obj = {a:100};
obj.name = 'apple';
obj.a = 10000;
console.log(obj); // {a:10000,name:'apple'}
进阶版(有时间要仔细看看,了解js变量的执行过程)
首先了解一下js变量的执行过程:
变量的声明有「创建、初始化和赋值」这三个过程
创建 =》初始化 =》 赋值
1、var 声明的变量执行过程
先看一段代码
function fn(){
var x = 1
var y = 2
}
fn()
在执行 fn 时,会有以下过程(不完全):
进入 fn,为 fn 创建一个环境。
找到 fn 中所有用 var 声明的变量,在这个环境中「创建」这些变量(即 x 和 y)。
将这些变量「初始化」为 undefined。
开始执行代码
x = 1 将 x 变量「赋值」为 1
y = 2 将 y 变量「赋值」为 2
也就是说 var 声明会在代码执行之前就将「创建变量,并将其初始化为 undefined」。
这就解释了为什么在 var x = 1 之前 console.log(x) 会得到 undefined。
创建变量(找到当前作用域中所有var声明的变量) =》初始化(为 undefined) =》 执行代码 =》 赋值
2、function 声明的变量执行过程
看一段代码
fn2()
function fn2(){
console.log(2)
}
JS 引擎会有一下过程:
找到所有用 function 声明的变量,在环境中「创建」这些变量。
将这些变量「初始化」并「赋值」为 function(){ console.log(2) }。
开始执行代码 fn2()
也就是说 function 声明会在代码执行之前就「创建、初始化并赋值」。
创建变量(找到当前作用域中所有function声明的变量) =》 初始化 =》 赋值 =》 执行代码
3. let 声明的变量执行过程
{
let x = 1
x = 2
}
我们只看 {} 里面的过程:
找到所有用 let 声明的变量,在环境中「创建」这些变量
开始执行代码(注意现在还没有初始化)
执行 x = 1,将 x 「初始化」为 1(这并不是一次赋值,如果代码是 let x,就将 x 初始化为 undefined)
执行 x = 2,对 x 进行「赋值」
这就解释了为什么在 let x 之前使用 x 会报错:
let x = 'global'
{
console.log(x) // Uncaught ReferenceError: x is not defined
let x = 1
}
原因有两个
console.log(x) 中的 x 指的是下面的 x,而不是全局的 x
执行 log 时 x 还没「初始化」,所以不能使用(也就是所谓的暂时死区)
看到这里,你应该明白了 let 到底有没有提升:
let 的「创建」过程被提升了,但是初始化没有提升。
var 的「创建」和「初始化」都被提升了。
function 的「创建」「初始化」和「赋值」都被提升了。
创建变量(找到当前作用域中所有let声明的变量) =》 执行代码 =》 初始化(为let声明的值,没有就为undefined) =》 赋值(修改值)
4.const 声明的变量执行过程
其实 const 和 let 只有一个区别,那就是 const 只有「创建」和「初始化」,没有「赋值」过程。
创建变量(找到当前作用域中所有const声明的变量,) =》 执行代码 =》 初始化为const声明的值,没有就为undefined
总结:
let 的「创建」过程被提升了,但是初始化没有提升。
var 的「创建」和「初始化」都被提升了。
function 的「创建」「初始化」和「赋值」都被提升了。
主要参考自:参考https://zhuanlan.zhihu.com/p/28140450
最后来一道综合小题:
var a = 1;
function foo() {
console.log(a);
a = 2;
console.log(a);
return;
function a() {};
}
foo();
console.log(a);
经过预编译可以看成以下形式:
var a = 1; // 定义一个全局变量 a
function foo() {
// 首先提升函数声明function a () {}到函数作用域顶端, 然后function a () {}等同于 var a = function() {};最终形式如下
console.log(a); // 打印局部变量 a 的值:function a () {}
var a = function () {}; // 定义局部变量 a 并赋值。
a = 2; // 修改局部变量 a 的值,并不会影响全局变量 a
console.log(a); // 打印局部变量 a 的值:2
return;
}
foo();
console.log(a); // 打印全局变量 a 的值:1
故而结果是:
a() {}
2
1
注意:函数提升只会提升函数声明,而不会提升函数表达式。