揭开提升神秘的面纱

549 阅读3分钟

相信很多人都会有这样的疑问,什么是提升? 如何理解这个概念呢? 记得大学第一堂c语言的课,老师在讲台上说的一句话,现在都还记忆尤新,“代码执行的顺序是由上到下”,但是在JavaScript中,真的是这样吗?我的回答是不完全正确的。

hello = 2;
var hello;
console.log(hello);

或许看完这段代码很多小伙伴会认为是undefined。

或许你错了...

我们来捋一捋这其中的细节, 在var hello声明在hello = 2的后面,很多人认为变量被重新赋值了,故被赋予默认值undefined。但是,结果却是2。

console.log(world)

var world = 3;

或许很多人认为结果是3, 抱歉你又错了! 结果是undefined

换一种思路思考

包括变量和函数在内的所有声明都会在任何代码被执行前被优先处理。

在此之前,可能很多小伙伴对声明这个概念还有疑惑,比如先调用函数再定义, 需要注意的是,每个作用域都会进行提升。

foo();

function foo() {
    console.log(a);  //undefined
    var a = 2;
}

foo();   // 不是ReferenceError  而是 TypeError

var foo = function bar(){ // do someThing }
函数声明

函数声明被提升,但是函数表达式却不会被提升, 为什么呢? 因为foo持有bar的内存地址,而foo被提升并分配给所在的作用域(全局) 因此foo()不会导致ReferenceError,但是foo此时未被赋值,这里需要补充的是, 假如它是一个函数声明而不是函数表达式,那么就会赋值,foo()由于对undefined值进行函数调用而导致非法操作便会抛出TypeError。

foo() //TypeError

bar() //ReferenceError 

var foo = function bar(){ //do someThing }


为什么是函数优先

函数声明和变量声明都会被提升,但是我们要明白一个细节,函数会被优先提升,其次才是变量

foo(); //1
var foo;

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

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

为什么会是1而不是2呢?

我们可以这样理解:

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

foo(); //1


foo =  function (){ console.log(2);}
重复定义

再看看下面的代码,我们可以得出结论: 尽管重复定义了foo,但是出现在后面的函数还是会把前面的覆盖的。

foo(); //3 

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

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

function foo(){ console.log(3);}

再看看下面的代码

foo(); TypeError: foo is not function 

var z = true;

if(z) {
  function foo() { console.log("z"); }
} else {
  function foo() { console.log("x"); }
}

得出结论

我们从上述代码中得出以下结论: var a = 2; 我们通常把它看成是一个声明,但是实际上引擎不是这样认为的,它会将var a = 2;当成两个单独的声明, 第一个是在编译阶段的任务,第二个才是执行阶段的任务。

其次是无论中用于声明在任何地方,都会降代码本身在执行前被优先进行处理,而这个过程可以理解成所有的声明(变量和函数),都会被移动到各自作用域的最顶端,这个过程叫做提升。

最后是声明本身也会被提升,但函数表达式的赋值在内的赋值操作并不会提升。