December 19, 2012

Javascript 的 Object 與 Function

雖然說只要翻兩下書看幾個網站就能開始寫 code,而且看了這些東西也不見得對於寫程式有一日千里的幫助。出於阿宅很純粹的好奇心,我還是花了點時間看了 ECMAScript  Q___Q

程式寫了一段時間,有時會對某一句天經地義的話沉思很久,總感覺此中有真意,欲辯「不知言」 - 不知道該怎麼解釋它,當然也有可能是庸人自擾,痴人發夢。往往好奇心就在此時被挑起,追尋了半天終於得到了一個原本就知道而且跟大家一模一樣的結論。或許此時只能用「過程比結果重要」來安慰自己。

從來沒想過自己會寫 javascript,剛碰 javascript 就讀到了不少讓人「津津樂道」的特色。好比 Hoisting 或 Closure 這種沒有在 spec 裡面出現,而是在實務中衍生出來的概念,又或著惡名昭彰的全域變數,一不小心就會跟它打上交道,看到不少說法都是因為 spec 定得不好。

這就有意思了,究竟是什麼樣的 spec 可以把一個語言搞成這樣?平常會用觀察的方式去理解一個語言的行為,但是 Javascript 的「出名」讓我有點擔憂,害怕自己觀察到的是某一個實作獨有的現象。因此才會去翻 ECMAScript 順便滿足好奇心。

好奇心被挑起之後,想要釐清以下幾個問題
  • 什麼叫做物件
  • 什麼叫做 function
  • 什麼是 function call, new,constructor
  • 產生新物件的過程為何
  • 產生的新物件與 constructor 的關係為何
在開始之前先說明,built-in 的 function 或 property, object 會以 [[Something]] 來表示,如果沒有理解錯誤,[[Prototype]] 就是實作中經常看見的 __proto__ 這個 property。

接下來是一點閱讀的心得與筆記,若有疏誤請不吝指教

4.2 Language Overview

4.2 指的是 spec 裡面的 4.2 章節,後面還會重複出現類似的數字。

在 Overview 的部份對一些常見且富含多種意義的名詞做了稍微明確解釋,先記起這些名詞往後會比較順利

  • ECMAScript program
    一群可以相互溝通的物件(is a cluster of communicating objects),程式就是在物件的互動之中完成功能。
  • ECMAScript object
  • 帶有一些 properties 的集合。(is a collection of properties each with zero or more attributes that determine how each property can be used)
  • Properties
    容器,可保有物件、函式或是 primitive value(are containers that hold other objects, primitive values, or functions.)
  • Primitive value
    • Undefined
    • Null
    • Boolean
    • Number
    • String
  • Object
    內建型別 Object 的成員(is a member of the remaining built-in type Object)
  • Function
  • 可以呼叫的 object (is a callable object.)
  • Method
    物件的 property 如果是個 function,稱之為 method(A function that is associated with an object via a property.)
而且 ECMAScript 定義了一組的 built-in objects
  • Object object
  • Function object
  • Array object
  • String object
  • Boolean object
  • Number object
  • Math object
  • Date object
  • RegExp object
  • JSON object
  • Error object
    • Error
    • EvalError
    • RangeError
    • ReferenceError
    • SyntaxError
    • TypeError
    • URIError
所謂的物件,就是 properties 的 collection。property 的概念上就像是 pointer,也就是說物件就是帶有一堆 pointer 的資料結構,這個 property 可能會指向 primitive value, function, object。

8.6.2 Object Internal Properties and Methods

這一章節指出,所有的物件都一定帶有某些 internal properties,這邊僅列出幾項
  • [[Prototype]]
  • [[Class]]
  • [[Get]]
  • [[GetOwnProperties]]
以 [[Class]] 為例,這個 property 會指向一個 String,表示「這是一個什麼樣的物件」,結果可能會是 Array, JSON, Object 或是 Function。

[[Get]] 指向一個 internal method,這個 method 需要一個參數叫 propertyName,執行的結果是回傳一個 property 的 value。

而 [[Prototype]] 就是 __proto__,至於它的值後面再說明。

此外,某些物件還有這些 internal properties
  • [[Constructor]] new operator 會用這個 property
  • [[Call]] 單純的 function call 會使用這個 property
  • [[Code]] 只有 Function Object 會有這個屬性
這些就是 Function Object 的 internal properties,至此終於知道「Function 也是 Object」這句話的意義:Function object 就是比 Object object 多了一些特定 internal properties 的物件。

接下來更深入地看一下產生 Function object 的過程

13.2 Creating Function Object

先定義名詞
    • FormalParameterList - 參數 list
    • FunctionBody - function 的內容
    Function object 產生的過程是
    1. 產生 native ECMASCript object, 令其名為 F
    2. 除了 [[Get]] 之外設定所有的 internal methods
    3. 把 F 的 internal property [[Class]] 設成 "Function"
    4. 把 F 的 internal property [[Prototype]] 設成 Function.prototype,即 built-in object Function 的prototype 屬性所指向的物件
    5. 把 [[Code]] 設成 FunctionBody
    6. 設定許多 internal properties,其中一些 property 會指向特定的 internal method,以下這兩個與 function 的行為有關
      • internal method [[Call]] 被 invoke 時這一次執行的狀態放入 funcCtx。[[Code]] 這個 internal property 就是 FunctionBody,執行之後的結果回傳,且把之前的執行狀態 restore 回來。
      • internal method [[Constructor]] 被 invoke 時
        1. 產生新的 ECMAScript object(假設叫 obj),並且設定一些 internal methods。
        2. 把 obj 的 [[Class]] 設成 "Object"
        3. 對 F 呼叫 [[Get]] property,參數是 "prototype",假設呼叫的結果叫做 proto
        4. 如果 proto 是一個 Object,把 obj 的 [[Prototype]] 指向該物件
        5. 如果 proto 不是一個 Object,把 obj 的 [[Prototype]] 指向 built-in Object 的 prototype 屬性
        6. 回傳值是 invoke [[Call]] 的結果並且把 this 指向新產生的 Obj 。因為 [[Call]] 其實是執行 Code,所以Constructor 與 Call 都同樣是去跑一遍 Code
    Function 也是物件,只是這個物件帶的屬性有點不一樣,因此成為一個可以呼叫(callable)的物件。

    產生 Function object 的時候,也會並為該物件設定一些 properties。好比把 [[Call]], [[Constructor]] [[Code]]。Code 的內容是 FunctionBody,[[Call]] 跟 [[Constructor]] 指向 internal method。

    執行這個 function 的時候(call function),就會 invoke [[Call]],[[Call]] 指向了 built-in function,作用是拿 [[Code]] 的值來執行。

    如果用 new 來執行 function,就會 invoke [[Constructor]]。spec 說 [[Constructor]] 也會去 inkvoke [[Call]],但是多了一些動作,譬如說設定 prototype,而且在 invocke [[Call]] 的時候會把 'this' 指到一開始產生的物件。

    所以 new 跟平常的 function call 各自執行不同的 internal method,前者會多做一些事情,譬如說把 this 指向剛剛產生的物件,以及設定 prototype。

    prototype property 跟 [[Prototype]] 是不一樣的東西。*.prototype 僅是一個 property,它什麼都可以指。而 [[Prototype]] 是 internal property,會指向 Constructor 的 prototype property,或是內建物件的 prototype property,總之就是會指向某一個物件,查找 property 的時候才有一個 chain 可以用。謹記這兩者的分別,閱讀 jQuery core 的時候會有幫助。

    No comments:

    Post a Comment