大厂面试题分享 面试题库
前后端面试题库 (面试必备) 推荐:★★★★★
地址:前端面试题库 web前端面试题库 VS java后端面试题库大全
最近帮公司招聘,主要负责一面,所以基本上问的基础多一点。但是我在问这样一道面试题的时候,竟然很少有人答对,这让我有点吃惊。话不多说,我们直接上题
题目
var foo = function () { console.log("foo1") } foo() var foo = function () { console.log("foo2") } foo() function foo() { console.log("foo1") } foo() function foo() { console.log("foo2") } foo()
这里我会要求面试者从上到下依次说出执行结果。很多面试者就直接愣住好几分钟,然后慌慌张张的给出一个错误的答案。普遍多的面试者给出的答案是:foo1、foo2、foo1、foo2。虽然在我看来这是一道简单的面试题,但是也不至于这么简单吧😱~~~
为了不成为一名令你讨厌的面试官,我觉得把你问住不是我的目的,我的目的是要给你讲清楚,😂
解答
拆分函数表达式
首先我会让面试者先看前面两个函数
var foo = function () { console.log("foo1") } foo() var foo = function () { console.log("foo2") } foo()
这时候大部分人基本上都可以答对了,是:foo1、foo2。再有很少数的人答不对那就只能”施主,出门右转“😌。接着根据我当时的心情可能会稍作追问(美女除外🙂):
foo() var foo = function () { console.log("foo1") }
这时候又有一部分的人答不上来了。这毫无疑问是肯定会报错的啊
我们都知道用var定义的变量会变量提升,所以声明会被拿到函数或全局作用域的顶部,并且输出undefined。所以当执行foo()的时候,foo还是undefined,所以会报错。由于js从按照顺序从上往下执行,所以当执行foo = function(){}
的时候,才对foo进行赋值为一个函数。我们重新看拆分之后的代码
var foo = function () { console.log("foo1") } foo() var foo = function () { console.log("foo2") } foo()
foo首先会变量提升,然后进行赋值为function。所以当执行第一个foo的时候,此时foo就是我们赋值的这个函数。接着执行第二个foo的赋值操作,由于函数作用域的特性,后面定义的函数将覆盖前面定义的函数。 由于在调用函数之前就进行了函数的重新定义,所以在调用函数时,实际执行的是最后定义的那个函数。所以上面的代码会打印:foo1、foo2。
这种定义函数的方式,我们称为函数表达式。函数表达式是将函数作为一个值赋给一个变量或属性
函数表达式我们拆分完了,下面就看看函数声明吧。
拆分函数声明
function foo() { console.log("foo1") } foo() function foo() { console.log("foo2") } foo()
大部分人其实都挂在了这里。函数声明会在任何代码执行之前先被读取并添加到执行上下文,也就是函数声明提升。说到这里其实大多数人就已经明白了。这里使用了函数声明定义了两个foo函数,由于函数声明提升,第二个foo会覆盖第一个foo,所以当调用第一个foo的时候,其实已经被第二个foo覆盖了,所以这两个打印的都是foo2。
讲到这里面试者都恍然大悟,哈哈,被我装到了吧!
总结
我们整体分析代码的执行过程
- 通过函数表达式定义变量
foo
并赋值为一个匿名函数,该函数在被调用时打印"foo1"。 - 接着,通过函数表达式重新定义变量
foo
,赋值为另一个匿名函数,该函数在被调用时打印"foo2"。 - 使用函数声明定义了两个名为
foo
的函数。函数声明会在作用域中进行提升。后面的会覆盖前面的 - 调用第一个
foo()
,输出"foo2"。 - 调用第一个
foo()
,也输出"foo2"。
其实就一个点: 函数表达式相对于函数声明的一个重要区别是函数声明在代码解析阶段就会被提升(函数声明提升),而函数表达式则需要在赋值语句执行到达时才会创建函数对象