前端小誌(轉型中)

一個用來記錄人老會忘記的地方

Javascript context & execution context

2018年03月25日
這邊試著整理一些javscript的context概念(教練我原本只想寫event loop啊,越寫越多orz...)

先說說js function,在w3c的定義中,呼叫一個function稱為invoke。

在看js文章與執行function的時候當會看到一個字 - 執行環境(Execution Context)

Execution Context就是執行javascript時候的環境(有講跟沒講一樣XD),更準確的來說,上下文環境,確認執行時候的this, var, method...

Execution Context有三種

  1. Gobal Execution Context - 一開始的地方,browser來說就是window
  2. Functional Execution Context - 執行function的地方
  3. Eval - 不常用

當js invoke一個function時,會從global開始呼叫function,並且建立execution context,如果有很多function,就會一層疊一層的,此稱為execution context stack,而我們執行function的方式稱之為call stack。

js建立execution context有兩個步驟

  1. 建立階段
  2. 執行階段

建立階段

  1. 建立activation object or the variable object variable object指的是存放execution context scope data的物件,是一個抽象的概念,在不同的context裡面實作方式(存放的東西)是不一樣的。 在global context中的variable object等同於自己,並且可以直接對變數引用,所以你能向下面一樣呼叫
var a = new String('test');

alert(a); // directly, is found in VO(globalContext): "test"

alert(window['a']); // indirectly via global === VO(globalContext): "test"
alert(a === this.a); // true

在function context中的VO稱之activation object,不能直接被引用,裡面要放入function arguments,context的var與function定義。

  • 對於每一個var,js會幫AO create一個property with value undefined,如果存在property就略過。

  • 建立property for arg,把arguments object丟進去(reference)

  • 對於function,js會create一個function,然後create property by function name,然後將ref丟進去,如果存在property就覆蓋。

  • 建立scope chain 想像成一個array或是list,將裡面塞入VO(包涵現在所在的) [bFunc VO, a FuncVO, Global VO] (es5會產生global scope, function scope,es6 let會產生block scope)

  • 確認this的指向。

最後

executionContextObj = {
    scopeChain: { /* variableObject + all parent execution context's variableObject */ },
    variableObject: { /* function arguments / parameters, inner variable and function declarations */ },
    this: {}
}

// example
FuncExecutionContextObj = {
  activationbj: {
      argumentObj : {
          0: e,
          length:1
      },
      a: 10,
      b: undefined,
      c: undefined
      Func: Pointer to the Function definition,
  },
  scopeChain: [Func variable object, Global exection context variable object],
  this: value of this
}

執行階段

從上逐步執行每一行code,若是跑到變數宣告才會賦值。

不過這樣很容易解釋

  1. var hoisting,因為create stage就已經知道變數了
  2. var hoisting但呼叫var時為undefined,因為實際給值的動作可能在呼叫之後
  3. function hoisting但能呼叫,因為function的處理方式是不一樣的
  4. function expression與function declaration是不一樣的,function declaration視為var 宣告,在執行階段跑到變數宣告才會create function

寫完文章才理解大家常常在說的上下文是"context"(翻了中英對照才知道...),實作中比較常會遇到this的指向問題,這邊並沒有多做著磨,我們應該少使用一些反模式就可以大幅減少遇到問題的可能性,像多用es6 module與arrow function就是一種好的寫法(大部份的function不用dynamic this對吧),有一些this太刁鑽了,想要的看這裡

後記

var a = {
  b() {
    const c = '12';
    console.log(this);
    function d() {
      console.log(c);
      console.log(this);
    }
    d();
  }
}

a.b();

// {b: ƒ}
// 12
// Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}

this的指向是跟著context的,而讀取變數是跟著scope的,上面範例,在執行d的時候,實際執行者是window,但變數是跟著scope chain所以能讀到c = 12。我發現之前我理解錯誤,我把context與execution context連想在一起,但在javascript這兩個詞應該是不一樣的(好模糊啊),execution context是scope VO加上"context"指向的this,所以我認為d是由b執行的,所以該指向execution context的this,但execution context實際上並沒有this的值,只有確認this的綁定。以d()來說,invoke一個unbounded function,其context指向global(global.d()),而execution context把global存起來,再想到callback形式的寫法( arr.map(function(){ }) ),就能得知其實是一樣的道理。

Context vs. Scope

The first important thing to clear up is that context and scope are not the same. I have noticed many developers over the years often confuse the two terms (myself included), incorrectly describing one for the other. To be fair, the terminology has become quite muddled over the years.

Every function invocation has both a scope and a context associated with it. Fundamentally, scope is function-based while context is object-based. In other words, scope pertains to the variable access of a function when it is invoked and is unique to each invocation. Context is always the value of the this keyword which is a reference to the object that “owns” the currently executing code.


展開Disqus
分類
最近文章
友站連結
© 2019 Ernie Yang