在文章开始之前,先来看看上面的两端代码的运行后果会是怎么:

function a(){    console.log('Hello');}a();function a(){    console.log('World');}a();

依照代码的书写程序,打印进去的货色应该先是 'Hello',而后再是 'World',但后果并不是如此,两次函数调用打印的都是 'World'

那换成函数表达式后果又会不一样

var a = function(){    console.log('Hello');}a();var a = function(){    console.log('World');}a();

这段代码的执行后果如下:

那到底是什么起因导致这两种函数的申明形式有这样的区别呢?

那是因为在函数执行之前,js引擎有一个预编译的过程。

预编译

预编译是在函数执行之前进行的,整个过程能够用四句话来形容:

  • 创立AO流动对象(Active Object);
  • 查找形参和变量申明,并赋予undefine;
  • 将实参和形参相对立;
  • 找函数申明,赋予变量指向函数的指针。

这里以上面这段代码来阐明这个过程:

function test1(a, b) {    console.log(a);      c = 0;    var c;    a = 3;    b = 2;    console.log(b);     function b() {}    function d() {}    console.log(b); }test1(1);
  1. 创立AO流动对象:

    AO{}
  2. 查找形参和变量申明,并赋予undefine:

    AO{ a: undefined, b: undefined, c: undefined}
  3. 将实参和形参相对立:

    AO{ a: 1, b: undefined, c: undefined}
  4. 找函数申明,赋予变量指向函数的指针:

    AO{ a: 1, b: function(){}, c: undefined, d: function(){}}

那么函数申明式定义和函数表达式定义在这个过程中的区别到底在哪里呢?

两者的区别就在于赋予变量指向函数的指针的机会不一样。

函数申明式定义是在预编译的最初一步将指向函数的指针赋予给了变量;而函数表达式定义就相当于是定义一个变量,在代码的预编译阶段变量始终为 undefined,最初在执行上下文执行的时候再给变量赋予指向函数的指针。

从上面的这段代码咱们能够看出,函数申明式定义在代码执行之前就曾经给变量赋予了指向函数的指针,而函数表达式定义却还是 undefine

function a(){    console.log('Hello');}console.log(b);var b = function(){    console.log('World');}

所以最开始的两端代码会有所区别的起因就是:

  • 第一段代码,在预编译阶段就曾经确定了变量指向的函数是哪一个函数,即第二次函数申明将第一次的笼罩掉了,所以变量始终指向第二个函数;
  • 第二段代码,在预编译阶段变量始终为 undefined,在函数执行时,再顺次赋予变量指向函数的指针。
以上均为作者的集体了解,如有问题请各位多多指出!

参考资料:
了解JavaScript中的执行上下文
js预编译