JavaScript 系列之做用域(三)

這是我參與8月更文挑戰的第7天,活動詳情查看:8月更文挑戰git

4、函數做用域和塊級做用域

ES6 以前 JS 沒有塊級做用域。github

if (true) {
  var name = 'zhangsan'
}
console.log(name) // zhangsan
複製代碼

從上面的例子能夠體會到做用域的概念,做用域就是一個獨立的地盤,讓變量不會外泄、暴露出去。上面的 name 就被暴露出去了,所以,ES6 以前 JS 沒有塊級做用域,只有全局做用域函數做用域markdown

var a = 100
function fn() {
  var a = 200
  console.log('fn', a) // fn 200
}
console.log('global', a) // global 100

fn()
複製代碼

ES6 出來以後,let 和 const 都可以聲明塊級做用域app

4.1 var

使用 var 聲明的變量,不管是在代碼的哪一個地方聲明的,都會提高到當前做用域的最頂部,這種行爲叫作變量提高(Hoisting)ide

  • 若是是在函數內部聲明的變量,都會被提高到函數開頭。
  • 若是是在全局聲明的變量,就會提高到全局做用域頂部。
function test() {
  console.log("1: ", a); //undefined
  if (false) {
    var a = 1;
  }
  console.log("3: ", a); //undefined
}

test();
複製代碼

實際執行時,上面的代碼中的變量 a 會提高到函數頂部聲明,即便 if 語句的條件是 false,也同樣不影響 a 變量提高。函數

function test() {
  var a;
  //a 聲明沒有賦值
  console.log("1: ", a); //undefined
  if (false) {
    a = 1;
  }
  //a聲明沒有賦值
  console.log("3: ", a); //undefined
}

test();
複製代碼

在函數嵌套函數的場景下,變量只會提高到最近的一個函數頂部,而不會提高到外部函數。oop

// b 提高到函數a頂部,但不會提高到函數 test
function test() {
  function a() {
    if (false) {
      var b = 2;
    }
  }
  console.log("b: ", b);
}
test(); // b is not defined
複製代碼

4.2 let

let 和 const 都可以聲明塊級做用域,用法和 var 是相似的,let 的特色是不會變量提高,而是被鎖在當前塊中。惟一正確的使用方法:先聲明,再訪問post

function test() {
  if (true) {
    console.log(a); //TDZ,俗稱臨時死區,用來描述變量不提高的現象
    let a = 1;
  }
}
test(); // a is not defined

function test() {
  if (true) {
    let a = 1;
  }
  console.log(a);
}
test(); // a is not defined
複製代碼

4.3 const

聲明常量,一旦聲明,不可更改,並且常量必須初始化賦值。ui

const 雖然是常量,不容許修改默認賦值,但若是定義的是對象 Object,那麼能夠修改對象內部的屬性值包括新增刪除鍵值對也是能夠的。lua

5、做用域鏈

函數有一個內部屬性 [[scope]]

自由變量一層一層向上尋找,直到找到全局做用域仍是沒找到,就宣佈放棄。這種一層一層的關係,就是做用域鏈

let a = 100;
function F1() {
  let b = 200;
  function F2() {
    let c = 300;
    console.log(a); // 自由變量,順做用域鏈向父做用域找
    console.log(b); // 自由變量,順做用域鏈向父做用域找
    console.log(c); // 本做用域的變量
  }
  F2();
}
F1();
複製代碼

image.png

image.png

文章連接

相關文章
相關標籤/搜索