《你不知道的JavaScript》(上卷)读书笔记:作用域和闭包1.5

提升

函数作用域块作用域的行为是一样的,可以总结为:任何声明在某个作用域内的变量,都将附属于这个作用域。

但是,作用域同其中的变量声明出现的位置有某种微秒的联系。

“先有鸡还是现有蛋”

直觉上认为,js代码在执行时是由上到下一行一行执行的,但是实际上并不完全正确。典型代码如下所示:

    a=2;
    var a;
    console.log(a);//2

这段代码的运行结果是2,不符合由上到下一行一行执行的预期!

我们再看另外一段代码:

console.log(a);//undefined
 var a=2;

这次,输出的结果是 undefined!也不符合由上到下一行一行的预期,借鉴上一段代码,有猜测认为它输出的是2,但是都不是!

到底发生了什么?是声明(蛋)在前还是赋值(鸡)在前? 

编译器再度来袭

js引擎会在解释js代码之前首先对其进行编译,编译阶段中的一部分工作就是找到所有声明,并用合适的作用域将它们关联起来

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

当遇到 var a=2; 时,但是js实际上会将其看成两个声明:var a; a=2;

前一个声明是在编译阶段进行的,第二个赋值声明会被留在原地等待执行阶段。

所以,本文第一段代码的等效代码如下:

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

第二段代码的等效代码如下:

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

这个过程,就像 变量和函数声明从它们在代码中出现的位置被“移动”到了最上面,这个过程叫做提升! 

先有蛋(声明),后有鸡(赋值)。

只有声明会提升!!而不是赋值语句被提升。

函数声明也会被提升,代码如下所示:

    foo();
    function foo(){
        console.log("hello");
    }

foo函数的声明被提升了,因此调用语句可以正常执行!

函数声明会提升,但是函数表达式却不会提升! 

我们看一下下面的代码:

console.log(foo);//undefined
    foo();//type error foo is not a function
    var  foo=function bar(){
        console.log("hello");
    }

这段foo的声明会被提升并分配给所在作用域,因此调用foo不会reference error而是typeError,直接输出会报undefined;

跟本文第二段代码效果一样,声明虽然被提升,但是赋值并未被提升。

即使是具名的函数表达式,名称标识符在赋值之前也无法在所在作用域中使用:

    foo();//Type error
    bar();//Reference error
    var  foo=function bar(){
        console.log("hello");
    }

这部分代码经过提升后,实际上会被理解成如下形式:

var foo;
    foo();//Type error
    bar();//Reference error
    foo=function (){
        var bar=...self...
        console.log("hello");
    }

函数优先

函数声明和变量声明都会被提升,但是函数声明会首先被提升,然后才是变量!

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

这段代码会输出1而不是2,这段代码会被引擎理解成如下形式:

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

注意!var foo 尽管出现在函数声明 function foo()的声明之前,但是他是重复的声明,因此被忽略了,因为函数声明会被提升到普通变量之前!

重复的var声明会被忽略,但是出现在后面的函数声明可以覆盖前面的!

foo();//2
function foo(){
    console.log("1");
}
function foo(){
    console.log("2");
}

 

你可能感兴趣的:(javaScript理论)