首先,我们先看看下面这段代码,输出的结果是什么?

  1. showName();
  2. console.log(name);
  3. var name = 'wang';
  4. function showName() {
  5. console.log(name);
  6. }

相信大家如果了解些JavaScript都能知道打印出来什么

image.png

当然大家也知道原因(变量提升)

1. 变量提升

在介绍变量提升之前我们先了解两个概念声明赋值

  1. var name = 'wang';

其中这段可以看成两段代码的组合:

  1. var name;// 声明 name 值为 undefined;
  2. name = 'wang';// 赋值 name 值为 'wang'

再来看看的函数声明赋值

  1. function foo() {
  2. console.log('foo')
  3. }
  4. var bar = function() {
  5. console.log('bar')
  6. }

javascript深入理解 - 图2

下面我们来说说变量提升:
变量提升:JavaScript代码执行过程中,JavaScript引擎把变量的声明部分和函数的声明部分提升到代码开头的’行为’。变量提升后,会给变量设置默认值,这个默认值就是undefined。

2. JavaScript代码的执行流程

从概念上的意思我们可能会认为’变量提升’是移动代码最前面。

实际上变量和函数声明在代码里位置是不会改变的,而且是在编译阶段被JavaScript引擎放入内存中。
**
一段js代码在执行之前需要被被JavaScript引擎编译,编译完成后,才会进入执行阶段。
javascript深入理解 - 图3

(1)编译阶段

我们以下面的例子来讲解

  1. var a = 2;
  1. 一旦V8引擎进入一个执行具体代码的执行上下文(函数),它就对代码进行词法分析或者分词。var a = 2;分解为 -> var 、a、 = 、2、;。

  2. 在对当前的整个作用域分析完成后,引擎将解析成一个AST(抽象语法树)。

  3. 当编译器开始进行代码生成的时候会进行如下操作:

  • 引擎每一次遇到声明,它就把声明传到作用域来创建一个绑定。对每一次声明它都会为变量分配内存。只是分配内存而不是把代码修改成声明提升。正如你所知道的,在JS中分配内存意味着将默认值设为undefined。

  • 在这之后,引擎每一次遇到赋值或者取值,它都会通过作用域查找绑定。如果在当前作用域中没有查找到就接着向上级作用域查找直到找到为止。

    (2)执行阶段

用最开始的例子我们来看下执行过程:

  1. 当执行到showName函数时候,JavaScript引擎便开始在当前作用域中查找,由于作用域中存在该函数的引用,所以JavaScript引擎开始执行该函数,并输出结果;
  2. 接下来执行console.log(name)javaScript引擎在作用域中查找,存在name变量,并且其值为undefined,所以输出undefined
  3. 接下来为name赋值,再去查找到name,并且进行赋值。

思考:代码中出现相同的变量或者函数怎么办?

  1. showName()
  2. function showName() {console.log('1 :', 1);}
  3. showName()
  4. function showName() {console.log('2 :', 2);}

结果都是2:2
后定义的覆盖前面的函数了。

思考2:相同名字变量和函数如何处理?

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

结果是报错
分析:

  1. 编译阶段遇到同名函数和变量,变量声明被忽略,进行函数声明。
  2. 在执行阶段先执行赋值showName = 1;将之前函数引用替换成1
  3. 执行打印正常打印1
  4. showName已经变成Number类型,再执行就会报错

再次看看下面例子,更加理解变量提升问题

  1. showName();
  2. var showName = function() {
  3. console.log(22222222)
  4. }
  5. function showName() {
  6. console.log(1111)
  7. }
  8. showName();

结果:

  1. 1111
  2. 22222222

分析:

  1. 首先编译阶段 变量和函数同名,那么在编译阶段,变量的声明会被忽略,所以未函数showName;
  2. 执行阶段:执行函数打印1111
  3. 赋值操作:函数变成更
  4. 执行新函数:22222222

下面是关于同名变量和函数的两点处理原则:

1:如果是同名的函数,JavaScript编译阶段会选择最后声明的那个。

2:如果变量和函数同名,那么在编译阶段,变量的声明会被忽略

3. V8引擎是如何执行一段代码