1. 程式人生 > >【ES6】塊級作用域與函式宣告

【ES6】塊級作用域與函式宣告

塊級作用域與函式宣告

ES5 規定,函式只能在頂層作用域和函式作用域之中宣告,不能在塊級作用域宣告。

// 情況一
if (true) {
  function f() {}
}

// 情況二
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 會被提升到函式頭部,實際執行的程式碼如下。

// ES5 環境
function f() { console.log('I am outside!'); }

(function
() { function f() { console.log('I am inside!'); } if (false) { } f(); }());

ES6 就完全不一樣了,理論上會得到“I am outside!”。因為塊級作用域內宣告的函式類似於let,對作用域之外沒有影響。但是,實際上在 ES6 瀏覽器中執行上面的程式碼,是會報錯的,這是為什麼呢?

原來,如果改變了塊級作用域內宣告的函式的處理規則,顯然會對老程式碼產生很大影響。為了減輕因此產生的不相容問題,ES6 在附錄 B裡面規定,瀏覽器的實現可以不遵守上面的規定,有自己的行為方式。

  • 允許在塊級作用域內宣告函式。
  • 函式宣告類似於var,即會提升到全域性作用域或函式作用域的頭部。
  • 同時,函式宣告還會提升到所在的塊級作用域的頭部。

根據這三條規則,在瀏覽器的 ES6 環境中,塊級作用域內宣告的函式,行為類似於var宣告的變數。

// 瀏覽器的 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 的瀏覽器中,都會報錯,因為實際執行的是下面的程式碼。

// 瀏覽器的 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() {}