可能你知道函数会有函数提升,也可能你知道函数有块级作用域,但这并不表示你知道的一定是正确的,不信的话,请继续看这篇文章。
首先我给出一段程序
console.log(fn);
fn();
{
fn();
function fn() {
console.log(10);
}
fn();
}
fn();
现在你在脑海中想象一下各个位置的输出结果。
下面公布答案
console.log(fn); // undefined
fn(); // 报错 fn is not a function
{
fn(); // 10
function fn() {
console.log(10);
}
fn(); // 10
}
fn(); // 10
如果你的答案和谷歌浏览器运行的结果完全相同,那么本文就没有必要继续看下去了。相反,请仔细阅读本文。
下面是正文
由于 ES5 和 ES6 在函数提升和函数块级作用域上有巨大差异,所以在这里我以 ES6 为标准。
第一步:我们要去了解什么是块级作用域,什么是 var 变量提升,什么是 let 暂时性死区。
如果你不会以上三点,请转移到我的这篇文章,并看完第 “一” 部分
第二步:记住这三句(浏览器遵循的ES6标准)
- 允许在块级作用域内声明函数
- 函数声明(这里指函数名称) 类似于 var,即会提升到全局作用域或函数作用域的头部
- 同时,函数声明(这里指函数整体) 还会提升到所在的块级作用域的头部
第三步:回到最开始的程序
再看一眼原程序:
console.log(fn);
fn();
{
fn();
function fn() {
console.log(10);
}
fn();
}
fn();
根据刚才说的第二句话,由于函数声明类似于 var ,所以程序会相当于这个样子:
var fn; // 根据第二句话,提升到了这儿
console.log(fn);
fn();
{
fn();
fn = function() {
console.log(10);
}
fn();
}
fn();
再根据刚才说的第三句话,程序会相当于这个样子:
var fn; // 根据第二句话,提升到了这儿
console.log(fn);
fn();
{
fn = function() { // 根据第三句话,提升到了这儿
console.log(10);
}
fn();
fn();
}
fn();
所以,程序运行的结果会是:
var fn; // 根据第二句话,提升到了这儿
console.log(fn); // undefined
fn(); // 报错 fn is not a function
{
fn = function() { // 根据第三句话,提升到了这儿
console.log(10);
}
fn(); // 10
fn(); // 10
}
fn(); // 10
回到原程序的结果:
console.log(fn); // undefined
fn(); // 报错 fn is not a function
{
fn(); // 10
function fn() {
console.log(10);
}
fn(); // 10
}
fn(); // 10
是不是一模一样的!
最后,我要提醒一句
我前面讨论的一直都是:函数声明 + 浏览器环境
即,function xxx() {} 这种函数声明。而不是 var xxx = function() {} 这种函数表达式。前者,用本文的逻辑去考虑函数的提升和函数的块级作用域;后者,等价于变量的提升和变量的块级作用域。
如果换做严格模式下的 Node 环境,那么将严格遵守 ES6 标准:函数声明类似于 let。
拿来吧你
觉得有用记得把赞留下!