ES6之块级作用域与函数声明

既然有了块级作用域,也有函数作用域,函数中有块级作用域,那么函数能不能在块级作用域中声明呢?这个问题真让人疑惑~

1、ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明

以下两种方法是错误的,既不是在顶层作用域,也不是在函数作用域中声明函数:

if (true) {
  function f() {}
}
try {
  function f() {}
} catch(e) {
  // ...
}

虽说以上两种情况在ES5中都是非法的声明方式,但是在浏览器中却没有报错,而是能够正常运行;

2、ES6中明确允许在块级作用域中声明函数

ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。

function f() { console.log('I am outside!'); }
//以下为在块级作用域中声明函数
(function () {  
  if (false) {
    // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }
  f();
}());

若在ES5中运行,则会发生函数声明提升的现象:

function f() { console.log('I am outside!'); }
//以下为在块级作用域中声明函数
(function () {  
  function f() { console.log('I am inside!'); }  //函数声明提升
  if (false) {
    
  }
  f();  //I am inside!
}());

在ES6环境中,理论上,会得到“I am outside”,'I am inside!'函数类似于let变量,则没有函数声明提升的过程,调用f()访问不到。
可是就算如此,我们能得到它本该输出的结果,但实际上,跑出来的结果是会报错的,而不能得到“I am outside”。

对于这个问题,ES6中有新的规定,ES6的浏览器的实现可以有自己的行为方式:
(1)允许在块级作用域内声明函数。
(2)函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
(3)同时,函数声明还会提升到所在的块级作用域的头部。

注意,上面三条规则只对 ES6 的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let处理。

// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
  if (false) {
    // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }
  f();
}());
// Uncaught TypeError: f is not a function

以上代码会报错是因为实际运行的是如下代码

// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
  var f = undefined;
  if (false) {
    function f() { console.log('I am inside!'); }
  }
  f();
}());
// Uncaught TypeError: f is not a function

考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。采用函数表达式的写法如下:

// 函数声明语句
{
  let a = 'secret';
  function f() {
    return a;
  }
}

// 函数表达式
{
  let a = 'secret';
  let f = function () {
    return a;
  };
}

另外,还有一个需要注意的地方。ES6 的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。

// 不报错
'use strict';
if (true) {
  function f() {}
}
// 报错
'use strict';
if (true)
  function f() {}
发布了28 篇原创文章 · 获赞 4 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_40736319/article/details/89212993