深刻解析JS变量声明和函数声明提高

不少时候,在直觉上,咱们都会认为JS代码在执行时都是自上而下一行一行执行的,可是实际上,有一种状况会致使这个假设是错误的。css

a = 2;
var a;
console.log(a);
复制代码

按照传统眼光,console.log(a)输出的应该是undefined,由于var a在a = 2以后。可是,输出的是2。html

再看第二段代码:前端

console.log(a);
var a = 2;
复制代码

有人会想到第一段代码,而后回答undefined。还有人会认为a在使用前未被声明,所以抛出ReferenceError异常。遗憾的是,结果是undefined。vue

为何呢?node

从编译器的角度看问题webpack

JS在编译阶段,编译器的一部分工做就是找到全部声明,并用合适的做用域将他们关联起来。对于通常人来讲var a = 2仅仅是一个声明,可是,JS编译器会将该段代码拆为两段,即:var a和a = 2。var a这个定义声明会在编译阶段执行,而a = 2这个赋值声明会在原地等待传统意义上的从上到下的执行。web

因此,在编译器的角度来看,第一段代码其实是这样的:面试

var a;  // 编译阶段执行
a = 2;
console.log(a);
复制代码

因此,输出的是2。bash

相似的,第二个代码片断其实是这样执行的:函数

var a;
console.log(a);
a = 2;
复制代码

这样的话,很明显,输出的应该是undefined,由于只对a进行了定义声明,没有对a进行赋值声明。

从上面这两个例子能够看出,变量声明会从它们在代码中出现的位置被移动到当前做用域的最上方进行执行,这个过程叫作提高。

函数提高

下面,再来看一段代码

foo();

function foo () {
    console.log(a);
    var a = 2;
}
在这个例子中,输出undefined而不会报错,由于,函数变量也能提高。即,实际上像以下的状况运行。

function foo () {
    var a;
    console.log(a);
    a = 2;//欢迎加入全栈开发交流圈一块儿学习交流:864305860
}

foo();
复制代码

说到这里,你是否是认为提高很简单,只要把变量都放到当前做用域最上方执行就行了?

下面,我来讲一种意外状况:函数表达式的提高状况。

函数表达式的提高状况

foo();

var foo = function bar () {
    console.log(a);
    var a = 2;
}
复制代码

你是否是想说,这个例子不是和以前的那个差很少吗?输出的固然是undefined呀。可是,结果是,不输出,由于JS报了TypeError错误!

由于,函数表达式不会进行提高!

该例子的实际运行状况是这样的:

var foo;
foo();
foo = function bar () {
    var a;
    console.log(a);
    a = 2;
}
复制代码

因为执行时,在做用域中找获得foo(该做用域最上方声明了foo),因此不会报ReferenceError错误,可是,foo此时没有进行赋值(若是foo是一个函数声明而不是函数表达式,那么就会赋值),也就是说实际上foo()是对一个值为undefined的变量进行函数调用,因此,理所应当抛出TypeError异常。

值得一提的是,即便是具名的函数表达式,名称标识符在赋值以前也没法在所在做用域中使用,即:

foo();  // TypeError
bar();  // ReferenceError

var foo = function bar () {}
复制代码

函数优先

函数声明和变量声明都会被提高,可是有一个值得注意的细节,那就是,函数会首先提高,而后才是变量!

看下面这一段代码:

foo();
var foo;
function foo () {
    console.log(1);
}
foo = function () {
    console.log(2);
}
复制代码

这一段代码会输出1,缘由就在于,函数优先。

这一段代码能够转换为如下形式:

function foo () {
    console.log(1);
}
var foo;    // 重复声明,被忽略
foo();      // 输出1
foo = function () {
    console.log(2);
}
复制代码

若是,在代码的结尾再执行一次foo函数,此时,输出的是1。

function foo () {
    console.log(1);
}
var foo;    // 重复声明,被忽略
foo();      // 输出1
foo = function () {
    console.log(2);
}
foo();      // 输出2
复制代码

由于,尽管重复的声明会被忽略了,可是后面的函数仍是能够覆盖前面的函数。

明白了这个道理,你就能够理解下面这个问题了:

foo();
var a = true;
if (a) {
    function foo () {
        console.log("a");
    }
} else {
    function foo () {
        console.log("b");//欢迎加入全栈开发交流圈一块儿学习交流:864305860
    }//面向1-3年前端人员
}//帮助突破技术瓶颈,提高思惟能力

复制代码

你猜这道题输出的结果是什么?是b!为何?由于foo进行了两次的声明,可是,后一次函数覆盖了前一次的函数。因此调用foo时,永远调用的都是console.log("b")。

总结

1.全部声明(变量和函数)都会被移动到各自做用域的最顶端,这个过程被称为提高

2.函数表达式等各类赋值操做并不会被提高

3.函数优先原则

4.尽可能避免产生提高问题

本次给你们推荐一个免费的学习群,里面归纳移动应用网站开发,css,html,webpack,vue node angular以及面试资源等。 对web开发技术感兴趣的同窗,欢迎加入Q群:864305860,无论你是小白仍是大牛我都欢迎,还有大牛整理的一套高效率学习路线和教程与您免费分享,同时天天更新视频资料。 最后,祝你们早日学有所成,拿到满意offer,快速升职加薪,走上人生巅峰。

相关文章
相关标签/搜索