JS中变量提升真的搞懂了吗?让你打脸的一道题

1,017 阅读4分钟

我们知道JS代码在执行之前,会做一系列的事情,其中就包括变量提升,原本以为把变量提升搞懂的我(因为这两天一直在研究变量提升,自我感觉已经很良好了,哈哈哈),拿到了一道打脸的题。当然了,拿给身边的程序员朋友们,做对的也......废话不多说,一起来看下这道题吧。

1. 题目

var a = 0;
if (true) {
    a = 1;
    function a({};
    a = 21;
    console.log(a)
}
console.log(a);

答案:21 1

2. 重新学习变量提升

2.1 var

首先说使用var声明的变量,只要那个变量是使用var声明的,那么在变量提升阶段要做的事情只有一个,就是去声明这个变量。

一道简单的题目看懂var的变量提升

console.log(a);
var a = 1;
console.log(a);
  • 在代码执行之前先创建一个变量a;此时并不会进行赋值等操作
  • 代码执行
    • console.log(a);因为此时已经有a这个变量了,只不过没有赋值,因此输出undefined
    • var a = 1;给变量a进行赋值为1
    • console.log(a);这时候再输出a的值,就是上面的赋值结果1

控制台查看输出结果

2.2 let const

我们知道使用let和const声明的变量没有变量提升,只有当代码走到那一行才会去执行声明等操作;

2.3 function(1)

在变量提升阶段function会声明+定义,这里需要提到的一个点就是,重复的定义,会以最后一个指向的堆内存为主。

下面使用一段代码来解释这句话的意思:

   fn();   
   function fn({console.log(1)};
   fn();  
   function fn({console.log(2)};
   fn(); 
   var fn = function ({console.log(3)};
   fn();  
   function fn({console.log(4)};
   fn();  
   function fn({console.log(5)};

var fn = function () {console.log(3)};这句话在变量提升阶段做的事情只是var了一个fn,右边的并没有执行;

这道题可以认真的思考下,可以很好的理解变量提升和函数。

2.4 function(2)

function fn(){...}没有在if/for等任何大括号内的时候,它会声明+定义,即:

  • 把右边的值存储在堆内存中,并把堆内存地址存储在栈内存;
  • 然后声明变量fn,
  • 最后让fn和堆地址关联

但如果把上面的这句话放在if/for这样的大括号内的时候,就变成下面这样的过程了:

  • 声明一个变量fn存储在栈内存中
  • 当满足条件进入到大括号内的时候
    • 第一件事情就是定义这个函数:即让这个变量名和堆地址进行关联。注意此时这个变量fn已经变为这个块内私有的变量了,和外面的fn没有任何关系;
    • 当代码执行过程中,遇到function fn(){...}的时候,它会去把全局中的fn修改一下,修改为堆中fn的值。修改完之后,后面对fn的操作又和全局的fn没任何关系

3.看穿题目的'庐山真面目'

这道题就是利用上面的所说到function(2)的变量提升情况。即当在if/for中存在函数,并且条件成立,那么这个函数就变为私有,直到遇到function fn(){...}的时候才会去操作全局的fn,其它情况操作的fn都是私有的。

下面是这道题的图解过程

4. 同类型题目的练习

{
  function foo(){}
  foo=1;
}
console.log(foo);
{
  function foo(){}
  foo=1;
  function foo(){}
}
console.log(foo);
{
  function foo(){}
  foo=1;
  function foo(){}
  foo=2;
}
console.log(foo);

5. 总结

这也是浏览器为了解决新老版本改善的function的变量提升机制,可以看出也是在一步步的完善,因此我们也要不断的学习,才可以跟上互联网的快速发展。