Successfully reported this slideshow.
Your SlideShare is downloading. ×

9. meta-programming

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Upcoming SlideShare
10. 進入瀏覽器
10. 進入瀏覽器
Loading in …3
×

Check these out next

1 of 58 Ad
Advertisement

More Related Content

Slideshows for you (20)

Similar to 9. meta-programming (20)

Advertisement

Recently uploaded (20)

Advertisement

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

×