ES6中块级作用域下的函数声明

背景

因为ES5的时候没有块级作用域,所以ES5规定不能再if这样的块中声明函数,但是为了兼容各大浏览器并没有严格遵守这条规定。

ES6的时候引入了块级作用域,规定在块级作用域中声明函数就相当于使用let来声明变量一样。但是又因为浏览器端的兼容问题,标准中说明浏览器端的实现可以不完全遵守,有自己的行为,如下:

  • 允许在块级作用域内声明函数。
  • 函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
  • 同时,函数声明还会提升到所在的块级作用域的头部。
if (false) {
  function a() {}
}
console.log(a);

上面输出 undefined这表明了上面两条,一个是允许在块级作用域内声明函数,一个是块级作用域内声明类似于var。

if (true) {
  console.log(a);
  function a() {}
}

输出函数a,这表明了第三条:函数声明会提升到所在块级作用域的头部。

问题和信息

上面现有的理论并不够解释下面的现象:

var a;

{
  a = 5;
  
  function a() {}
  
  a = 0;
  
  console.log(a);
}
console.log(a);

chrome最新版输出的是:第一个console输出的是0,第二个console输出的是5

先陈述下依据已知的三条理论得出上面代码几个不合理的现象:

  1. 首先稍微变通就知道如下代码:
{
  function a() {}
}

console.log(a);

这里打印了a是一个函数,穿透了块级作用域。上面并没有解释这种现象。

  1. a = 0;的赋值并没有影响到块外部的a。

解释上面两个问题,还需要额外的信息(信息来自stackoverflow高赞回答,原文在参考链接中):

function enclosing() {
  {
     function compat() {}
  }
}

// works the same as

function enclosing() {
  var compat₀ = undefined; // function-scoped
  {
     let compat₁ = function compat() {}; // block-scoped
     compat₀ = compat₁;
  }
}

上面对块中的函数声明引入的额外的概念,更加清晰明了的解释了底层做了什么。

首先额外引入的信息本身也存在一些问题。少了函数会提升到当前块作用域顶部,我认为应该如下修改:

function enclosing() {
  var compat₀ = undefined; // function-scoped
  {
     function compat() {}
     let compat₁ = compat; // block-scoped
     compat₀ = compat₁;
  }
}

这样就修复了没有函数提升的问题。

猜想

根据额外补充的知识加上自己的想象力得到如下结果:

var a₀;

{
  // 这部分被提升
  function a() {}
  let a₁ = a;
  a₀ = a;
  // 这部分被提升END
  
  a₀ = 5;
  
  a₁ = 0;
  
  console.log(a₁);
}
console.log(a₀);

a₀ 和 a₁ 都是a在不同位置的不同分身。

参考

发布了48 篇原创文章 · 获赞 52 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/letterTiger/article/details/102840601