Skip to content

块级作用域与函数声明 ​

本节目标:理解“块级作用域与函数声明”的核心思路,并能把它用于实际开发或面试表达。

ES5 规定
函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。
// 情况一:if语句if (true) {  function f() {}}
// 情况二:trycatch语句try {  function f() {}} catch(e) {  // ...}
上面两种函数声明,根据 ES5 的规定都是非法的。

但是: 浏览器没有遵守这个规定,为了兼容以前的旧代码,还是支持在块级作用域之中声明函数,因此上面两种情况实际都能运行,不会报错。

**ES6****块作用域声明函数**
ES6明确允许在块级作用域之中声明函数。块级作用域之中的函数声明语句的行为类似于let,在块级作用域之外不可引用。
function f() { console.log('I am outside!'); }
(function () {  if (false) {   // 重复声明一次函数f    function f() { console.log('I am inside!'); }  }
f();}());
上面代码在 ES5 中运行,会得到“I am inside!”,因为在if内声明的函数f会被提升到函数头部。
ES6 就完全不一样了,理论上会得到“I am outside!”。因为块级作用域内声明的函数类似于let,对作用域之外没有影响。但是,如果你真的在ES6浏览器中运行一下上面的代码,是会报错的,这是为什么呢?
// 浏览器的 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在[附录](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-block-level-function-declarations-web-legacy-compatibility-semantics) B里面规定,浏览器的实现可以不遵守上面的规定,有自己的[行为方式](http://stackoverflow.com/questions/31419897/what-are-the-precise-semantics-of-block-level-functions-in-es6)。
允许在块级作用域内声明函数。

函数声明类似于var,即会提升到全局作用域或函数作用域的头部(只声明不定义)。

同时,函数声明还会提升到所在的块级作用域的头部。

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

根据上述规则,上面的例子实际运行的代码如下:
// 浏览器的 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;  };}

本站总访问 — 次