面试题目(腾讯):
下面题目输出结果是什么?
var a=2;
function a() {
console.log(3);
}
console.log(typeof a);
答案解析:
这道题目主要考察JS的变量提升和函数提升相关的知识点
1. 变量提升
在ES6
之前,JavaScript
没有块级作用域(一对花括号{}
即为一个块级作用域),只有全局作用域和函数作用域。变量提升即将变量声明提升到它所在作用域的最开始的部分。下面两个示例包含了变量提升的两种情况(全局作用域和函数作用域):
示例 1
console.log(a);
var a = 8;
// 变量提升后等价于下面代码
var a;
console.log(a) // undefined
a = 8;
示例 2
var a = 8;
function fn() {
console.log(a);
var a = 9;
console.log(a);
}
// 变量提升后等价于下面代码
var a = 8;
function fn() {
var a;
console.log(a);
a = 9;
console.log(a);
}
2. 函数提升
js中创建函数有两种方式:函数声明式和函数字面量式。只有函数声明才存在函数提升。
示例
console.log(f1);
console.log(f2);
function f1() {}
var f2 = function() {}
// 函数提升后等价于下面代码
function f1() {}
var fn;
console.log(f1); // function f1() {}
console.log(f2); // undefined
f2 = function() {}
3. 变量提升 VS 函数提升
上面的面试题目就属于这种类型,变量和函数同名。当两种提升都存在的情况下,结果是什么样的呢?
var a=2;
function a() {
console.log(3);
}
console.log(typeof a);
// 变量提升和函数提升后,代码变为
function a() {
console.log(3);
}
a=2;
console.log(typeof a); // number
为什么是这样呢?这就需要从JS的编译过程解释了。
在JS代码执行前,会执行词法分析。所以JS运行要分为词法分析和执行两个阶段。
函数在运行的瞬间,会生成一个活动对象Active Object
,简称AO
。
- 分析形参
- 如果函数有形参,则给当前活动对象增加属性,赋值为
undefined
。
- 如果函数有形参,则给当前活动对象增加属性,赋值为
- 分析变量
- 如果AO上还没有 XXX 属性,则给当前活动对象增加属性,赋值为undefined.
- 如果AO上有 XXX 属性,则不做任何影响。
- 分析函数
- 把函数赋值给 AO.func 属性
- 如果此前 func 属性已存在,则覆盖。(证明函数的优先级比较高)
面试题目分析系过程大致如下:
- 分析形参
- 因为是全局环境,可以理解为自执行函数,没有形参。
- 分析变量
- 按照2.1的规则,
AO.a = undefined
- 按照2.1的规则,
- 分析函数
- 按照3.2的规则直接覆盖掉a属性,
AO.a = function(){console.log(3)}
- 按照3.2的规则直接覆盖掉a属性,
4. 为什么会有提升
Brendan Eich给出了答案:
- 由于第一代JS虚拟机中的抽象纰漏导致的,编译器将变量放到了栈槽内并编入索引,然后在(当前作用域的)入口处将变量名绑定到了栈槽内的变量。(注:这里提到的抽象是计算机术语,是对内部发生的更加复杂的事情的一种简化。)
- 函数提升就是为了解决相互递归的问题,大体上可以解决像ML语言这样自下而上的顺序问题。
总体意思就是:变量提升是人为实现的问题,而函数提升在当初设计时是有目的的。
补充:
js中无论哪种形式声明(var,let,const,function,function*,class
)都会存在提升现象,不同的是, var,function,function*
的声明会在提升时进行初始化赋值为 undefined
,因此访问这些变量的时候,不会报ReferenceError
异常,而使用 let,const,class 声明的变量,被提升后不会被初始化,这些变量所处的状态被称为“temporal dead zone
”,此时如果访问这些变量会抛出ReferenceError
异常,看上去就像没被提升一样。
总结:
- js会将变量的声明提升到js顶部执行,因此对于这种语句:
var a = 2;
其实上js会将其分为var a;
和a = 2;
两部分,并且将var a
这一步提升到顶部执行。 - 变量提升的本质其实是由于js引擎在编译的时候,就将所有的变量声明了,因此在执行的时候,所有的变量都已经完成声明。
- 当有多个同名变量声明的时候,函数声明会覆盖其他的声明。如果有多个函数声明,则是由最后的一个函数声明覆盖之前所有的声明。
扫一扫 关注我的公众号【前端名狮】,更多精彩内容陪伴你!
网友评论