作用域和作用域鏈

作用域

在 JavaScript 中有兩種作用域

  • 全局作用域
  • 局部作用域

當變量定義在一個函數(shù)中時,變量就在局部作用域中,而定義在函數(shù)之外的變量則從屬于全局作用域。每個函數(shù)在調(diào)用的時候會創(chuàng)建一個新的作用域。


全局作用域

最外層函數(shù)定義的變量擁有全局作用域,全局作用域里的變量能夠在其他作用域中被訪問和修改。

var name = "Tennant";
console.log(name);//Tennant

function logName(){
    console.log(name);
}
logName();//Tennant

局部作用域

和全局作用域相反,局部作用域一般只在固定的代碼片段內(nèi)可訪問到,而對于函數(shù)外部是無法訪問的,最常見的例如函數(shù)內(nèi)部

function fn(){
    var name = "Tennant";
}
fn();
console.log(name);//undefined

需要注意的是,函數(shù)內(nèi)部聲明變量的時候,一定要使用var命令。如果不用的話,實際上聲明了一個全局變量:

function fn(){
    name = "Tennant";
}
fn();
console.log(name);//Tennant

再如以下代碼,只要函數(shù)內(nèi)定義了一個局部變量,函數(shù)在解析的時候都會將這個變量“提前聲明” :

var scope = "global";
      function fn(){
         console.log(scope);//undefined
         var scope = "local";
         console.log(scope);//local
      }
      fn();
var scope = "global";
      function fn(){
         var scope;//提前聲明了局部變量
         console.log(scope);//undefined
         scope = "local";
         console.log(scope);//local
      }
      fn();

沒有塊級作用域

Javascript沒有塊級作用域,在其他類C的語言中,由花括號封閉的代碼塊都有自己的作用域,因此支持條件來定義變量。例如,以下代并不會得到想要的結(jié)果:

if(true){
    var name = "Tennant";
}
console.log(name);//Tennant

這里是在一個if語句中定義了變量name。如果是在C、C++或者Java中,name會在if語句執(zhí)行完畢后被銷毀。但在JavaScript中,if語句中的變量聲明會將變量添加到當前的執(zhí)行環(huán)境(在這里是全局環(huán)境)中。在使用for語句時要特別注意,例如:

for (var i = 0; i < 10; i++){
    doSomething(i);
}
console.log(i);//10

對于有塊級作用域的語言來說,for語句初始化變量的表達式所定義的變量,只會存在于循環(huán)的環(huán)境之中。而對于JavaScript來說,由for語句創(chuàng)建的變量i即使在for循環(huán)執(zhí)行結(jié)束后,也依舊會存在于循環(huán)外部的執(zhí)行環(huán)境中。


作用域鏈(Scope chain)

當代碼在一個環(huán)境執(zhí)行時,會創(chuàng)建變量對象的一個作用域鏈。作用域鏈的作用是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。


執(zhí)行環(huán)境

執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了它們各自的行為。每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象(variable object),環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中。

全局執(zhí)行環(huán)境是最外圍的執(zhí)行環(huán)境,全局執(zhí)行環(huán)境被認為是window對象,因此所有的全局變量和函數(shù)都作為window對象的屬性和方法創(chuàng)建的。

JavaScript的執(zhí)行順序是根據(jù)函數(shù)的調(diào)用來決定的,當一個函數(shù)被調(diào)用時,該函數(shù)環(huán)境的變量對象就被壓入一個環(huán)境棧中。而在函數(shù)執(zhí)行之后,棧將該函數(shù)的變量對象彈出,把控制權(quán)交給之前的執(zhí)行環(huán)境變量對象。

舉個例子:

var color = "blue";
function changeColor(){
    if(color === "blue"){
        color = "red";
    }else{
        color = "blue";
    }
}
changeColor();
alert(color);//red

在以上例子中,函數(shù)changeColor()的作用域鏈包含兩個對象:

它自己的變量對象(其中定義著arguments對象)和全局環(huán)境的變量對象??梢栽诤瘮?shù)內(nèi)部訪問變量color,就是因為可以在這個作用域鏈中找到它。

看如下代碼:

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() //輸出多少

//輸出a=2
//執(zhí)行fn2函數(shù),fn2找不到變量a,接著往上在找到創(chuàng)建當前fn2所在的作用域fn1中找到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() //輸出多少

//輸出undefined
//函數(shù)fn2在執(zhí)行的過程中,先從自己內(nèi)部找變量找不到,再從創(chuàng)建當前函數(shù)所在的作用域fn去找,注意此時變量聲明前置,a已聲明但未初始化為undefined

以上查找方向為:

  1. 函數(shù)在執(zhí)行的過程中,先從自己內(nèi)部找變量(注意找的是變量的當前的狀態(tài))
  2. 如果找不到,再從創(chuàng)建當前函數(shù)所在的作用域去找, 以此往上
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 任何程序設(shè)計語言都有作用域的概念,簡單的說,作用域控制著變量與函數(shù)的可見性和生命周期。ES6之前,JS變量的作用域...
    卓三陽閱讀 695評論 0 2
  • 前天被問到作用域鏈的知識,感覺有個大概的認識,但是轉(zhuǎn)化為語言就無法調(diào)理清楚地講述出來,回來后決定惡補功課,在此做個...
    栗子酥小小閱讀 369評論 0 0
  • 作用域是JavaScript最重要的概念之一,想要學好 JavaScrip t就需要理解JavaScript作用域...
    云音流閱讀 271評論 0 0
  • 一、作用域 所謂作用域,簡單來說就是變量和函數(shù)的可訪問范圍。舉個例子: 上面test函數(shù)中的變量a擁有一個函數(shù)作用...
    Da_xiong閱讀 220評論 0 1
  • 那天夜里的晚安文/洛小簡 那天夜里的晚安,被藏在風雨里,我以為她會收到。驚醒后的夜空不太美,雨又玷污我的窗,風又侵...
    洛小簡閱讀 217評論 0 0

友情鏈接更多精彩內(nèi)容