做用域和做用域鏈

做用域:

變量所在的上下文,指的是變量在哪些地方能夠訪問前端

對於JavaScript來講有全局做用域可是沒有塊級做用域,在ES6中引入了關鍵字let能夠生成塊做用域.見如下代碼:後端

var value = true
if (value) {
  var age = 18
  console.log(`我今年${age}歲了`)
}
console.log (`我也是${age}歲了哦`)

//輸出  "我今年18歲了"   "我也是18歲了哦"
// 可見在if()中不存在塊級做用域

在這裏if()裏的變量具備全局做用域,全局皆可以使用函數

var value = true
if (value) {
  let age = 18
  console.log(`我今年${age}歲了`)
}
console.log (`我也是${age}歲了哦`)

/*輸出
  "我今年18歲了"
  "error"
  "ReferenceError: age is not defined*/

在這裏使用關鍵字let 使 if () 塊裏的變量age產生了塊級做用域,使得它只在這個塊裏生效.學習

JS中有函數做用域,指的是做用域在函數內部。這裏一共說了三種做用域,其實能夠說是兩種:一種是全局做用域,而是局部做用域(函數做用域、塊級做用域),塊級做用域概念又包括了函數做用域。code


簡要說下幾個做用域聲明方式:
  1. 全局做用域:在全部函數外部使用var語句聲明變量或者在聲明變量時忽略var則會隱式轉化爲全局變量
  2. 函數做用域: 須要在函數內部使用var 聲明變量才行
  3. 塊級做用域: 在變量名前添加let語句聲明(ES6)

做用域鏈

var a = "你好,我是a";
function scopeChain(a) {
  var b =1;
  function inScope(a) {
     var c = "螞蟻"
     console.log(`大象愛${c}`)
     console.log(`我是最內層的函數,這裏也可使用a: ${a}`)
  }
  console.log(`能使用a嗎?${a}`)
  inScope(a)
}
scopeChain(a)

這裏a是全局做用域下的變量,b是函數scopeChain()做用域下的變量,而c是函數scopeChain()裏的inScope()函數做用域下的變量。對象

做用域鏈的前端始終是當前環境做用域下變量對象,逐層往外做用域連接,最後端是全局變量環境下的變量,這些變量時連接在一塊兒,在解析一個變量時從鏈前端日後端搜索(從內不找外部找),可是有一點值得注意:每一個變量的做用域老是從自身聲明的做用域往外找,而不是調用它的地方ip

var a = 1
function fn1(){
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
function fn2(){
  console.log(a)
}
var fn = fn1()
fn()

 //輸出1

這裏就是當fn1() 執行後調用 fn2() 時,發現fn3()做用域下沒有,也就是做用域鏈前端沒有,往外找一層也就是fn1()做用域下進行查找,做用域也就日後端前進了一步,發現仍是沒有,繼續往外層做用域查找找到了全局做用域,也就是做用域鏈的最後端,找到了後調用它。作用域

可是對於fn2()來講它須要調用a這個變量,這裏也就出現了誤區:在fn3()裏有變量a,那麼是用的是這個變量a嗎?io

  • 但其實fn2()是發現不了這個變量a的,由於fn2()聲明的地方並不在fn3()裏,同理fn1()也不是fn2()聲明的地方,因此對於fn2()來講它只發現了全局下的 var a =1 因此調用它並輸出a時也就等於1.

var a = 1
function fn1(){
  function fn2(){
    console.log(a)
  }
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn()

//輸出多少?
  • 由以上的論述能夠分析出這段代碼,當fn1()被調用時也就是 return fn3 ,也就調用了fn3(),而後fn3()裏又是調用fn2(),而fn() 是在fn1()裏聲明的,天然也就使用了fn1()裏的變量a,又由於a變量在調用前聲明並賦值了,故此輸出爲2

var a = 1
function fn1(){

  function fn3(){
    function fn2(){
      console.log(a)
    }
    var a

    fn2()
    a = 4
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn()

//輸出多少?

分析這段代碼,發現與上面代碼不一樣之處在於先聲明瞭 ` var a 後沒有裏脊賦值,在調用了fn2()後再進行的賦值,那麼這裏應該是多少呢?console

  • 這裏也就牽涉到了聲明前置,對於fn3()下,當聲明 var a時,也就是執行到了fn3()代碼前,函數聲明和變量聲明會提早至代碼前端,因此這裏聲明並無影響到輸出值得改變,可是賦值操做是按照程序順序執行的,當調用前,a只聲明沒有賦值,則會輸出undefined。 而具體變量查找是符合做用域鏈的順序來的.

總結以下:

  1. 函數在執行的過程當中,先從本身內部找變量
  2. 若是找不到,再從建立當前函數所在的做用域去找, 以此往上
  3. 注意找的是變量的當前的狀態

我的學習備忘,若有謬誤,歡迎指正。

相關文章
相關標籤/搜索