【自种树自乘凉】这才是真正的函数提升|8月更文挑战

380 阅读2分钟

可能你知道函数会有函数提升,也可能你知道函数有块级作用域,但这并不表示你知道的一定是正确的,不信的话,请继续看这篇文章。

首先我给出一段程序

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新特征总结

第二步:记住这三句(浏览器遵循的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。

拿来吧你

觉得有用记得把赞留下!

img-1625963389999dcfe689eb070971738326e76339c8aa0.jpg