👩‍🚀前端面试|JS基础(一)之变量提升

1,238 阅读4分钟

前言

本人刚刚入职前端,也大大小小经历了很多面试,想把一些重要的基础知识与面试题结合起来做个总结。由于大部分面试题中都会隐藏着许多基础的JS知识,所以讲解JS基础的内容基本都会包含两部分:面试题和知识点。本人会尽力从面试题切入,然后再阐述面试题中涉及到的相关知识点,为了方便大家理解,也会尽量举出实例和画出流程图。最后,欢迎大家批评与交流。

面试题

今天讲到的知识点是变量提升,这是一个与JS执行流程相关,并且无处不在的东西。首先给出相关面试题,大家先看看能否做出正确答案吧!如果不能,那就仔细看看后续的知识点讲解哦!!

(1)

(function(){
   var x = y =1;
 })()
var z;

console.log(y); //1,由于y=1,y是一个全局变量,所以y能正常输出
console.log(z); //undefined
console.log(x); //Uncaught ReferenceError: x is not defined

(2)

function fn1(){
  console.log('fn1')
}
var fn2

fn1()//fn1
fn2()//fn2 is not a function

fn2 = function(){
  console.log('fn2')
}

fn2()//fn2

知识点

定义

变量提升是指在JavaScript代码执行过程中,JS引擎会将变量的声明部分和函数的声明部分提升到开头的“行为”。提升后,会给变量设置默认值,即undefined

下面是几行简单的代码,将以他为例子,讲解变量提升的过程的原理。

showName()
console.log(myname)
var myname = '小趴菜'
function showName(){
   console.log('函数showName被执行')
}

原理

首先在定义中说到了提升,那这到底是一种什么样的行为呢?真的是在代码层面上把声明部分提升到最前面了嘛?

实际上不是的,这就涉及到JS代码的执行流程,如下图所示:

未命名文件.png

这里的提升是指包括变量和函数在内的所有声明都会在JS代码的编译阶段被JS引擎放入内存中

  • 编译阶段是指词法分析、语法分析、代码生成等一系列过程,这里不细讲这部分

经过编译后,会生成两部分代码,执行上下文和可执行代码。执行上下文是指一段JS代码的执行环境,变量提升的内容就会存储在该执行上下文中。

编译阶段

对应的在例1中的代码在编译阶段后会生成:

执行上下文中:
var name = undefiend 
function showName(){
  console.log("函数showName被执行了")
}
可执行代码:
showName()
console.log(myname)
myname = "小趴菜"
执行阶段
  • JS引擎开始执行“可执行代码”的时候,当执行到showName函数,JS引擎便在执行上下文中查找该函数,由于在编译阶段已经存在该函数的饮用,所以JS引擎就会执行该函数,并输出“函数showName被执行”的结果。
  • 执行到console.log(myname)时,就会在其执行上下文中查找到myname变量,并输出undefined
  • 最后,将“小趴菜”赋值给myname

变量提升的原因及带来的问题

原因

  1. 在JS代码执行之前,会进行语法检查和预编译,并且这一操作只进行一次。这么做就是为了提高性能,如果没有这一步,那么每次执行代码前都必须重新解析一遍该变量(函数),而这是没有必要的,因为变量(函数)的代码并不会改变,解析一遍就够了。
  2. 增加了容错性,如果出现了变量在声明之前使用也不会有太大的问题

问题

var string = 'xiaopacai'
for(var i=0; i < string.length; i++){
  console.log(string[i])
}
console.log(i)//9

由于变量提升,i变成了全局变量,本来i只是用计数,但在循环结束后i并没有被销毁

var a = 3
function fn(){
   console.log(a)
   if(false){
      var a = "hello"
   }
}

fn()//undefined

由于变量提升,内层定义的a会进行变量提升覆盖外层的a,导致输出undefined

ES6改进

ES6就引入了let和const命令,创建块级作用域,由let和const定义的变量就不存在变量提升