Javascript之变量提升和函数提升

Posted by Shen Chaoran on July 1, 2018

首先需要声明的是,JavaScript 并不能简单地成为解释型语言,应该是半编译型半解释型的语言? JavaScript 引擎会在正式执行前进行一次预编译,在这个过程中,会将变量声明和函数声明提升到当前作用域的顶端,然后在执行后面的代码。

变量提升

变量的声明只有一种方式,那就是用 var 关键字声明,用 let 不会出现变量提升。 直接赋值不是一种声明方式,这仅仅是在全局对象上创建了新的属性(而不是变量),不会提前声明。而且直接赋值实在执行阶段创建的。

var foo = 3;

function hoistVariable() {

    var foo = foo || 5;

    console.log(foo); // 5
}

hoistVariable();

被转换为:

var foo = 3;

//预编译之后
function hoistVariable() {
    var foo;

    foo = foo || 5;

    console.log(foo); // 5
}

hoistVariable();

函数提升

函数声明有几张方式:

  • 匿名函数:function a(){},这种方式会有提前声明,而且提前声明的优先级最高,也就是说如果有变量和他同名时,a 表示的是函数而不是变量值。
  • 通过 Function 构造:var a = new Function('a', 'b', 'return a+b;')。这种方式和变量提升相同。
  • 具名函数:var a = function (){}。这种方式和变量提升相同。

变量和函数同时提升时,会先提升变量,再提升函数,所以有同名变量和函数时,函数会覆盖同名变量的值。 js中创建函数有两种方式:函数声明式和函数字面量式。只有函数声明才存在函数提升!

console.log(f1); // function f1() {}   
console.log(f2); // undefined  
function f1() {}
var f2 = function() {}
function hoistFunction() {
    foo(); // output: I am hoisted

    function foo() {
        console.log('I am hoisted');
    }
}

hoistFunction();

被转换为

//预编译之后
function hoistFunction() {
    function foo() {
        console.log('I am hoisted');
    }

    foo(); // output: I am hoisted
}

hoistFunction();
//当函数声明遇到函数表达式时
function hoistFunction() {
    foo(); // 2
    var foo = function() {
        console.log(1);
    };
    foo(); // 1
    function foo() {
        console.log(2);
    }
    foo(); // 1
}

hoistFunction();

被转换为

//预编译之后
function hoistFunction() {
    var foo;
    foo = function foo() {
        console.log(2);
    }
    foo(); // 2
    foo = function() {
        console.log(1);
    };
    foo(); // 1
    foo(); // 1
}

hoistFunction();