js做用域

詞法做用域

定義與查找

詞法做用域就是定義在詞法階段的做用域,簡單來講詞法做用域是由你在寫代碼時將變量和塊做用域寫在哪裏來決定的,所以大部分狀況下當詞法分析器處理代碼時會保持做用域不變javascript

function foo(a) {
  var b = a * 2;
  function bar(c) {
    console.log(a, b, c)
  }
  bar(b * 3)
}
foo(2) // 2, 4, 12

此例中一共由三個逐級嵌套的做用域:java

  1. 包含這整個全劇做用域,其中只有一個標識符:foo
  2. 包含着foo所建立的做用域,其中有三個標識符:a、bar和b
  3. 包含着bar所建立的做用,其中只有一個標識符:c

做用域查找過程編程

  1. 當引擎執行console.log(a, b, c)聲明時,它首先會從最內層的bar()函數做用域開始查找,在這裏找到了c
  2. 由於沒法找到a和b,所以會到再上一層的foo()做用域去查找

做用域查找會在找到第一個匹配的標識符時中止
不管函數在哪裏被調用,也不管它如何被調用,它的詞法做用域都只由函數被聲明時所處的位置決定編程語言

函數做用域

函數做用域指的是屬於這個函數的所有變量均可以在整個函數的範圍內使用及複用,包括嵌套的做用域,這種設計方案很是有用。函數

隱藏內部實現

在軟件設計中,應該最小限度地暴露必要內容,而將其餘內容都隱藏起來,這個原則在如何在如何選擇做用域來包含變量和函數也一樣適用,例如:設計

var b;
function one(a) {
  b = a + another(a)
  console.log(b)
}
function another(a) {
  return a*2
}

one(2) // 6

在這段代碼中,給予外部函數b和another訪問權限不只沒有必要,並且可能會被以非預期的方式使用,所以更加合理的設計會將這些內容隱藏在one()內部,例如:code

function one(a) {
  function another(a) {
    return a*2
  }
  var b
  b = a + another(a)
  console.log(b)
}
one(2) //6

這樣設計b和another()都沒法從外部被訪問,功能和最終效果都沒有受影響,可是設計上將具體內容私有畫ip

當即執行行數表達式

經過前面的介紹已經知道,在任意代碼片斷外部添加包裝函數,能夠將內部變量和函數定義隱藏起來,外部做用域沒法訪問包裝函數內部的任何內容。例如:作用域

var a = 0
function ex() {
  var a = 1
  console.log(a) // 1
}
ex();
console.log(a)// 0

雖然這樣能夠解決一部分問題,可是並不理想,會將ex這個變量污染全局做用域,而且須要調用才能運行其中的代碼,javascript提供瞭解決問題的方案:當即執行函數表達式(IIFE)io

var a = 0;
(function () {
  var a = 1
  console.log(a) //1
})();
console.log(a) //0

此時就不會有函數名泄漏到全局做用域,而且會自動執行,同時外部的做用域也沒法直接訪問當即執行函數內的變量

塊做用域

事實上javascript在ES6以前並無塊做用域的概念,可是其餘不少編程語言都支持塊做用域,ES6以前javascript循環:

for (var i=0; i<3; i++) {
  console.log(i) //0, 1, 2
}
console.log(i) //3

雖然咱們的代碼看起來i只會在for循環內部使用,可是很不幸,i會被泄漏到全局的做用域中,污染全局做用域,即便這並是咱們本意,可是在ES6改變了現狀,引入了新的let關鍵字,提供了除var之外的另外一種變量聲明方式,let能夠將變量綁定到所在的任意做用域中,即爲其聲明的變量隱式地劫持了所在的做用域

for (let i=0; i<3; i++) {
  console.log(i) //0, 1, 2
}
console.log(i) // i is not defined

以上內容是我的的一點總結,若是有錯誤或不嚴謹的地方,歡迎批評指正,若是喜歡,歡迎點贊收藏

相關文章
相關標籤/搜索