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在不同位置的不同分身。

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章