JavaScript 系列之做用域(一)

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

1、執行上下文 (EC)

當 JavaScript 代碼執行一段可執行代碼(executable code)時,會建立對應的執行上下文(execution context),能夠理解是一個對象java

當執行 JS 代碼時,會產生三種執行上下文:數組

  • 全局執行上下文
  • 函數執行上下文
  • eval 執行上下文

每一個執行上下文中都有三個重要的屬性:markdown

  • 變量對象(Variable object,VO),當進入執行上下文時,這時候尚未執行代碼,變量對象包含變量函數聲明函數的形參,該屬性只能在全局上下文中訪問
  • 做用域鏈(Scope chain),JS 採用詞法做用域,也就是說變量的做用域是在定義時就決定了
  • this

2、執行上下文棧 (ECS)

爲了管理執行上下文,因此 JavaScript 引擎建立了執行上下文棧(Execution context stack,ECS)。app

2.1 例一

var a = 10;
function foo(i) {
  var b = 20;
}
foo();
複製代碼

對於上述代碼,執行棧中有兩個上下文:全局上下文函數 foo 上下文ide

試想當 JavaScript 開始要解釋執行代碼的時候,最早遇到的就是全局代碼,因此初始化的時候首先就會向執行上下文棧壓入一個全局執行上下文,咱們用 globalContext 表示它,而且只有當整個應用程序結束的時候,stack 纔會被清空。函數

stack = [ globalContext, fooContext]
複製代碼

對於全局上下文來講,VO 大概是這樣的:post

globalContext.VO === globe
globalContext.VO = {
  a: undefined,
  foo: <Function>,
}
複製代碼

對於函數 foo 來講,VO 不能訪問,只能訪問到活動對象(AO)ui

// arguments 是函數獨有的對象(箭頭函數沒有)
// 該對象是一個僞數組,有 `length` 屬性且能夠經過下標訪問元素
// 該對象中的 `callee` 屬性表明函數自己
// `caller` 屬性表明函數的調用者
fooContext.VO === foo.AO
fooContext.AO = {
  i: undefined,
  b: undefined,
  arguments: <> } 複製代碼

對於做用域鏈,能夠把它理解成包含自身變量對象和上級變量對象的列表,經過 [[Scope]] 屬性查找上級變量this

fooContext.[[Scope]] = [
  globalContext.VO
]

fooContext.Scope = fooContext.[[Scope]] + fooContext.VO

// so
fooContext.Scope = [
  fooContext.VO,
  globalContext.VO
]
複製代碼

2.2 例二

function fun3() {
  console.log("fun3");
}
function fun2() {
  fun3();
}
function fun1() {
  fun2();
}
fun1();
複製代碼

當執行一個函數的時候,就會建立一個執行上下文,而且壓入執行上下文棧,當函數執行完畢的時候,就會將函數的執行上下文從棧中彈出。知道了這樣的工做原理,讓咱們來看看如何處理上面這段代碼:

// 僞代碼

// fun1()
ECStack.push(<fun1> functionContext);
// ECStack = [globalContext, fun1Context]

// fun1中居然調用了fun2,還要建立fun2的執行上下文
ECStack.push(<fun2> functionContext);
// ECStack = [globalContext, fun1Context, fun2Context]

// 擦,fun2還調用了fun3!
ECStack.push(<fun3> functionContext);
// ECStack = [globalContext, fun1Context, fun2Context, fun3Context]

// fun3執行完畢
ECStack.pop();
// ECStack = [globalContext, fun1Context, fun2Context]

// fun2執行完畢
ECStack.pop();
// ECStack = [globalContext, fun1Context]

// fun1執行完畢
ECStack.pop();
// ECStack = [globalContext]

// javascript接着執行下面的代碼,可是ECStack底層永遠有個globalContext
複製代碼
相關文章
相關標籤/搜索