如果不在控制台输入以上代码(编号1-5),你认为在控制台打印的结果分别是什么?
揭晓答案:100
、100
、undefined
、undefined
、Uncaught TypeError: fn is not a function
;
假设之前没有接触过JS
或是还没有学习该知识点。就会有这样的疑问❓“这结果似乎不合理”(后续实际生产中写代码声明变量在绝大部分情况会使用let
和 const
而不是使用var
)
- 示例3️⃣为什么不报错。
var x = 10;
语句是置于console.log(x);
的后面,打印一个不存在的值,居然不报错(正常情况是报错:Uncaught ReferenceError: x is not defined)? - 示例4️⃣和示例5️⃣应该都要报错才对,为什么偏偏只有示例5️⃣报错呢?
下面通过案例分析来剖析答案
案例分析
var变量声明提升
示例3分析
从写代码的视角出发,【var x = 10;】这就是一个简单的变量声明并给变量赋值的语句。但是实际上JS编译器会做如下处理
- 分解代码
var a;
、a =2;
- 编译器会查询当前作用域是否已经存在一个变量
a
,如果不存在则会要求当前作用域集合新增一个变量a
,如果存在则忽略。 - 编译器将
var a; a = 2;
生成引擎运行这个赋值操作需要的代码,JS引擎在当前作用域中如果查询到变量a
,则会将2
赋值给变量a
。
综上,所以示例2、示例3的代码实际上的大致处理流程如下:
// 示例2
var x;
x = 100;
console.log(x);
// 示例3
var x;
console.log(x);
x = 100;
所以变量声明与赋值其实分开的,从上面两个代码处理的流程可以得知变量声明会被在当前作用域置顶,也就是常说的声明前置(提升)。
需要注意:示例3中,只有变量声明提升了,而赋值逻辑的位置并没有发生任何移动。
函数声明提升
示例4、5分析
大致可以推断出示例4️⃣、示例5️⃣的代码的处理流程
// 示例4 处理流程
fn = 函数体fn // 绑定函数到鱼当前函数名相同的标识符上
fn();
function fn() {
var x;
console.log(x);
x = 2;
}
// 示例5 处理流程
var fn;
fn();
fn = function(){
var x;
console.log(x);
x = 2;
}
示例4️⃣之所以在控制台打印的值为undefined
而没有报错的原因是:一 函数fn
已通过函数声明定义,在代码执行之前JavaScript引擎会访问声明的函数fn
接着注册与函数名同名的函数标识符,也就是函数声明前置(函数声明提升,即在函数声明之前可调用函数);二 函数内部的声明的变量也被提升到顶部(函数fn
内部顶部)。
示例5️⃣之所以报错是因为,函数表达式中声明的变量fn
会存在变量提升,此时的fn
只是一个变量且没有被赋值,其值为undefined
。一个变量的值为为undefined
,却使用了常规函数调用的方法fn()
,则控制台必然会报错“Uncaught TypeError: fn is not a function”。
注意: 函数表达式、箭头函数并不会出现函数声明提升的情况。