var与let、const的区别及变量提升(函数提升)过程解析

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

注意:函数提升只会提升函数声明,而不会提升函数表达式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值