Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

9. meta-programming

38 views

Published on

探索物件特性
判斷物件型態
認識Reflect API
結合Proxy與Reflect API

Published in: Technology
  • Be the first to comment

  • Be the first to like this

9. meta-programming

  1. 1. 9. meta-programming • 學習目標 – 探索物件特性 – 判斷物件型態 – 認識Reflect API – 結合Proxy與Reflect API 2
  2. 2. • meta前置詞源於希臘文,原本相當於之後 (after)、超越(beyond)之意 • 後續衍生出許多意義,像是關於(about) – metadata是「與資料相關的資料」(data about data) 3
  3. 3. • meta-programming可以對比為「與程式 設計相關的程式設計」(programming about programming) • 不同於靜態的data,programming是動態 的,「meta-programming從事的程式設 計,會影響另一個程式設計」 4
  4. 4. • 就JavaScript而言… • 檢視程式行為、修改程式行為,甚至是修 改語言預設行為等,可以令程式受到影響 的程式設計 5
  5. 5. 修補API 6
  6. 6. • 要運用這種手法,必須是特性值不會被設 為Falsy family成員的情況 • 如果無法避免這類情況,必須做更嚴格的 檢查 7
  7. 7. 8
  8. 8. 特性/值清單 • for..in針對的是實例本身以及繼承而來 的可列舉(emnuerable)特性 • propertyIsEnumerable() 9
  9. 9. • Object.keys() • Object.values() • Object.entries() 10
  10. 10. • Object.getOwnPropertyNames() • Object.getOwnPropertySymbols() 11
  11. 11. 特性順序 • EnumerateObjectProperties規範只從實 例本身以及原型鏈取得可列舉特性,for in就是實作之一 • 沒有規範順序排列問題,因此for in列舉 的特性順序因不同引擎實作而有所差別 • Object.keys()的順序是依賴在實作品 12
  12. 12. • [[OwnPropertyKeys]]規範如何取得自身 特性,也定義了順序 – 會先考慮數字索引,以數字遞增方式取得,接 著是特性被建立的順序,然後才是符號被建立 的順序 • 採用的函式有… – Object.getOwnPropertyNames() – Object.getOwnPropertySymbols() – Reflect.ownKeys() 13
  13. 13. 物件型態 • 對於基本型態、函式、undefined、 Function實例的話,許多場合會使用 typeof,對於其他的物件型態 • 如果想判斷物件是否屬於某個繼承關係, 常見使用instanceof • 在沒有進一步定義下,instanceof的判 斷依據是原型鏈 14
  14. 14. • Symbol.hasInstance靜態方法傳回值決 定了使用instanceof判斷時,受測實例 是否被視為此類別型態 15
  15. 15. constructor特性 • 不是個別實例擁有,而是建構式或類別 prototype上的特性 • 也常見作為型態檢測的依據 • constructor可以修改 • 使用原型鏈實現繼承時,建議設定 constructor指向子建構式 • 使用類別語言定義子類別時,會自動設定 constructor特性 16
  16. 16. Object.prototype.toString() • ECMAScript規範要求呼叫後,必須傳回 '[object class]'格式的字串 17
  17. 17. • 基於對標準的支持,不少程式庫也會使用 這個來作為標準內建型態的判斷依據 • ES5以前,沒有方式可以調整'[object class]'格式中'class'的部份 • Object.prototype.toString()實際 上是取得引擎內部實作[[Class]]的資訊 18
  18. 18. • ES6在定義建構或類別時,可以定義 Symbol.toStringTag特性,決定 '[object class]'格式'class'的部份 19
  19. 19. 20
  20. 20. 真陣列? • Array.isArray()就是根據內部實作特 性[[Class]]的值'Array‘ • 在ES5剛釋出的那個年代,若想修補 Array.isArray()該怎麼做呢? 21
  21. 21. • 真正的Array.isArray()看的是[[Class]] 22
  22. 22. Symbol.species • 陣列的方法執行後若傳回陣列,使用類別 語法繼承Array的子型態,呼叫這類方法 後傳回的物件也會是子型態 • 指定子類別要使用哪個類別來建構實例 23
  23. 23. 24
  24. 24. 25
  25. 25. 物件相等性 • == • === • SameValueZero • SameValue 26
  26. 26. • Set元素、Map的鍵在比較相等性時,使用 SameValueZero演算 – 採用===,然而NaN等於NaN,0等於-0。 • SameValue演算與SameValueZero演算不 同的地方只在於,0不等於-0, 27
  27. 27. • 採用SameValue演算的API之一是 Object.is()函式 28
  28. 28. • Object.defineProperty()、 Object.defineProperties() 29
  29. 29. • 多數API基本上採用===就好了 • 如果真的不能避免NaN,或者必須區分0與- 0,就要留意SameValueZero與 SameValue的差別 30
  30. 30. Reflect與Proxy • ECMAScript規範[[GetPrototypeOf]]、 [[SetPrototypeOf]]、[[IsExtensible]]等 • 在實現JavaScript引擎時,這類演算實現為 引擎的內部方法(Internal method) • 在ES5時,可以存取內部方法的API,隨意 地配置為Object的函式 31
  31. 31. • ES6以後,提供了Reflect API作為存取內 部方法的統一管道,並提供更多的控制細 節 • ES6以後也新增Proxy API,提供了干涉內 部方法的時機點,可干涉的點與Reflect API一對一對應 • 透過Proxy與Reflect的各種結合,就可 為 meta-programming提供諸多的可能性 32
  32. 32. Reflect API • Object被取代的函式 – Reflect.getPrototypeOf() – Reflect.setPrototypeOf() – Reflect.isExtensible() – Reflect.preventExtensions() – Reflect.defineProperty() – Reflect.getOwnPropertyDescriptor() 33
  33. 33. • 與Object上相對應的函式,功能上基本相 同 • 略作調整的地方 – Object有些對應的函式執行完會傳回物件本 身,而Reflect會傳回布林值 – Object對應的函式target參數若是基本型態, 會使用對應的類別實例來包裹基本型態值,然 而,Reflect在此時會拋出TypeError 34
  34. 34. • 在ES6以後,建議使用Reflect的這些方法, 來取代Object對應的方法 • 未來與內部方法存取相關的函式,只會在 Reflect上配置 35
  35. 35. 36
  36. 36. 進階函式控制 • func.apply(self, args)這類呼叫, 隱含地假設func實例本身沒有定義 apply()方法 • 若想不想發生同名問題,方式之一是使用 Function.prototype.apply.apply( func, [self, args]) 37
  37. 37. • ES6以後,可以使用Reflect.apply()來 達到相同目的 • Reflect.apply(func, self, args) 38
  38. 38. • 定義取值函式或設值函式,如果想控制 this的對象呢? 39
  39. 39. • ES6以後,可以使用Reflect.get()或 Reflect.set() 40
  40. 40. • Reflect.get()或Reflect.set()存取 的是[[GET]]、[[SET]]內部方法 • 純綷用來當成是存取特性值也是可行的 41
  41. 41. 運算子的對應函式 • in、delete、new運算子使用到內部方法 • 對應函式Reflect.has()、 Reflect.deleteProperty()、 Reflect.construct() 42
  42. 42. • 非嚴格模式下,對於不可組態的特性, delete刪除失敗會傳回false,若是嚴格 模式,刪除失敗會引發TypeError • Reflect.deleteProperty() 成功的 話會傳回true,刪除失敗會傳回false 43
  43. 43. • 在建構物件時會使用new運算子, Reflect.construct()是對應的函式 44
  44. 44. • 可以指定建構式中new.target的實際建 構式或類別 45
  45. 45. • new.target代表建構式,建構式的 prototype,會成為實例的原型 46
  46. 46. Proxy API • 希望能在存取物件特性時進行 console.log(),記錄有哪些特性被存取了? • Proxy實例會捕捉(Trap)並呼叫處理器上 定義的對應方法 47
  47. 47. 48
  48. 48. 代理模式 • Reflect的函式,與Proxy處理器可設定 的捕捉方法是一對一對應的 • Proxy實例可捕捉內部方法被操作的時機, 在處理器中Reflect用來存取內部方法 49
  49. 49. • 實作上提供代理物件給客戶端操作,代理 物件具有與目標物件相同介面 • 在客戶端對實作毫不知情下,不會意識到 正在操作的是代理物件 • 代理物件可以單純地將任務轉發給目標物 件,或者是在這前後附加額外的邏輯 50
  50. 50. • 明顯地被違反物件既有的協定,試圖實作 代理會拋出TypeError 51
  51. 51. 52
  52. 52. this又是誰? 53
  53. 53. 54
  54. 54. 55
  55. 55. 56
  56. 56. 57
  57. 57. • 想要有個可撤消的(Revocable)的Proxy 實例,可使用Proxy.revocable()函式 58

×