O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

Metaprogramming in JavaScript

1.417 visualizações

Publicada em

An introduction to metaprogramming and an overview of some of the ES6 features that enable Reflective Metaprogramming in JavaScript.

Publicada em: Engenharia
  • Entre para ver os comentários

Metaprogramming in JavaScript

  1. 1. Metaprogramming in JavaScript Web Directions Summit | November 17
  2. 2. Hello! I am Mehdi Valikhani Software Engineer at Autopilot
  3. 3. Metaprogramming?!
  4. 4. “Metaprogramming is a programming technique in which computer programs have the ability to treat programs as their data. It means that a program can be designed to read, generate, analyse or transform other programs or itself while running source: Wikipedia
  5. 5. “Metaprogramming is a programming technique in which computer programs have the ability to treat programs as their data. It means that a program can be designed to read, generate, analyse or transform other programs or itself while running source: Wikipedia
  6. 6. Reflection
  7. 7. “In computer science,reflection is the ability of a computer program to examine,introspect, and modify its own structure and behaviour at runtime. Reflection is a valuable language feature and it facilitates metaprogramming. source: Wikipedia
  8. 8. Language features that enables reflection 1. Evaluate a string as if it were a source code statement at runtime. 2. Convert a string matching the symbolic name of a class or function into a reference to or invocation of that class or function. 3. Discover and modify source code constructions (such as code blocks, classes, methods, protocols, etc.) as first-class objects at runtime. source: Wikipedia
  9. 9. Reflective JavaScript Metaprogramming
  10. 10. Reflective JavaScript Metaprogramming Using ES5 features
  11. 11. Metaprogramming features of ES5 Code Evaluation eval() Examination ➔ instanceof ➔ typeof ➔ Object.getOwnPropertyNames() Modification Getter, setter
  12. 12. Reflective JavaScript Metaprogramming Using ES6 features
  13. 13. Symbol ReflectProxy
  14. 14. Symbols The new type in JavaScript
  15. 15. ➔ The seventh type of values in JavaScript Symbol ➔ The Symbol() function returns a value of type symbol ➔ Symbol() always returns unique values ➔ They may be used as an identifier for object properties source: MDN
  16. 16. Example: Using Symbol as property identifier const nameField = Symbol('beer name'); const beer = { [nameField]: 'VB!' }; console.log(beer[nameField]); // -> VB!
  17. 17. Example: Using Symbol as property identifier const nameField = Symbol('beer name'); const beer = { [nameField]: 'VB!' }; console.log(beer[nameField]); // -> VB!
  18. 18. Example: Using Symbol as property identifier const nameField = Symbol('beer name'); const beer = { [nameField]: 'VB!' }; console.log(beer[nameField]); // -> VB!
  19. 19. Example: Using Symbol as property identifier const nameField = Symbol('beer name'); const beer = { [nameField]: 'VB!' }; console.log(beer[nameField]); // -> VB!
  20. 20. Well-known Symbols ➔ A set of built-in JavaScript symbols ➔ Represent internal language behaviours ➔ They were not exposed to developers before source: MDN
  21. 21. Symbols are used by ES6 to enable “Reflection within implementation”. Developers include them on their existing classes and objects to change the default behaviour of an operation or action. source: keithcirkel.co.uk
  22. 22. What can be done using well-known symbols ➔ Control the behaviour of instanceof for an implemented class ➔ Manipulate the behaviour of for of when iterated over a class ➔ Control over Array.concat ➔ Custom matches for String.match(), String.replace(), String.search() and String.split() ➔ Control the behaviour of Javascript when converting objects to primitive types Check MDN for a list of well-known symbols
  23. 23. Example: Using well-known Symbol.hasInstance to modify behaviour of “instanceof” class MyArray { } const friends = ['foo', 'bar']; console.log(friends instanceof MyArray); // -> false class MyArray { static [Symbol.hasInstance](object) { return Array.isArray(object); } } const friends = ['foo', 'bar']; console.log(friends instanceof MyArray); // -> true
  24. 24. Example: Using well-known Symbol.toPrimitive for a ShoppingBasket class class ShoppingBasket { constructor() { this.items = []; } add(title, quantity, price) { this.items.push({ title, quantity, price}); } total() { return this.items.reduce(function(accumulator, item) { return accumulator + (item.price * item.quantity); }, 0); } } } const basket = new ShoppingBasket(); basket.add('bread', 2, 2.5); basket.add('milk', 1, 1.4);
  25. 25. Example: Using well-known Symbol.toPrimitive for a ShoppingBasket class class ShoppingBasket { constructor() { this.items = []; } add(title, quantity, price) { this.items.push({ title, quantity, price}); } total() { return this.items.reduce(function(accumulator, item) { return accumulator + (item.price * item.quantity); }, 0); } [Symbol.toPrimitive](hint) { if (hint === 'number') return this.total(); return this; } } const basket = new ShoppingBasket(); basket.add('bread', 2, 2.5); basket.add('milk', 1, 1.4); console.log(+basket); // -> 6.4 console.log(new Number(basket)); // -> 6.4
  26. 26. Example: Using well-known Symbol.toPrimitive for a ShoppingBasket class class ShoppingBasket { constructor() { this.items = []; } add(title, quantity, price) { this.items.push({ title, quantity, price}); } total() { return this.items.reduce(function(accumulator, item) { return accumulator + (item.price * item.quantity); }, 0); } [Symbol.toPrimitive](hint) { if (hint === 'number') return this.total(); return this; } } const basket = new ShoppingBasket(); basket.add('bread', 2, 2.5); basket.add('milk', 1, 1.4); console.log(+basket); // -> 6.4 console.log(new Number(basket)); // -> 6.4
  27. 27. Proxy
  28. 28. Proxy, as the name indicates, provides “Reflection through interception”. It wraps objects and intercepts their behaviours through traps. source: keithcirkel.co.uk
  29. 29. What can be done using Proxy ➔ A trap for delete operator ➔ Manipulate the behaviour of in operator ➔ Control over getting and setting property values ➔ A trap for function calls ➔ A trap for new operator Check MDN for a list of Proxy features
  30. 30. Example: Implementation of a virtual field using Proxy const student = { firstName: 'Jackson', lastName: 'Rowe', }; const studentProxy = { get: function(object, fieldName) { if (fieldName === 'fullName') { return `${object.firstName} ${object.lastName}`; } return object[fieldName]; } }; const proxiedStudent = new Proxy(student, studentProxy); console.log(proxiedStudent.fullName); // -> Jackson Rowe
  31. 31. Example: Implementation of a virtual field using Proxy const student = { firstName: 'Jackson', lastName: 'Rowe', }; const studentProxy = { get: function(object, fieldName) { if (fieldName === 'fullName') { return `${object.firstName} ${object.lastName}`; } return object[fieldName]; } }; const proxiedStudent = new Proxy(student, studentProxy); console.log(proxiedStudent.fullName); // -> Jackson Rowe
  32. 32. Example: Implementation of a virtual field using Proxy const student = { firstName: 'Jackson', lastName: 'Rowe', }; const studentProxy = { get: function(object, fieldName) { if (fieldName === 'fullName') { return `${object.firstName} ${object.lastName}`; } return object[fieldName]; } }; const proxiedStudent = new Proxy(student, studentProxy); console.log(proxiedStudent.fullName); // -> Jackson Rowe
  33. 33. Example: Implementation of a virtual field using Proxy const student = { firstName: 'Jackson', lastName: 'Rowe', }; const studentProxy = { get: function(object, fieldName) { if (fieldName === 'fullName') { return `${object.firstName} ${object.lastName}`; } return object[fieldName]; } }; const proxiedStudent = new Proxy(student, studentProxy); console.log(proxiedStudent.fullName); // -> Jackson Rowe
  34. 34. Example: Value validation using Proxy const student = { firstName: 'Jackson', lastName: 'Rowe', }; const studentProxy = { set: function(object, fieldName, value) { // validation logic for the “age” field if (fieldName === 'age') { if (typeof value !== 'number') { throw TypeError('only numbers are accepted'); } if (value <= 0) throw TypeError('that is impossible'); } object[fieldName] = value; return true; } }; const proxiedStudent = new Proxy(student, studentProxy); proxiedStudent.age = 'a'; // errors proxiedStudent.age = -1; // errors proxiedStudent.age = 12; // does not error
  35. 35. Example: Value validation using Proxy const student = { firstName: 'Jackson', lastName: 'Rowe', }; const studentProxy = { set: function(object, fieldName, value) { // validation logic for the “age” field if (fieldName === 'age') { if (typeof value !== 'number') { throw TypeError('only numbers are accepted'); } if (value <= 0) throw TypeError('that is impossible'); } object[fieldName] = value; return true; } }; const proxiedStudent = new Proxy(student, studentProxy); proxiedStudent.age = 'a'; // errors proxiedStudent.age = -1; // errors proxiedStudent.age = 12; // does not error
  36. 36. Example: Value validation using Proxy const student = { firstName: 'Jackson', lastName: 'Rowe', }; const studentProxy = { set: function(object, fieldName, value) { // validation logic for the “age” field if (fieldName === 'age') { if (typeof value !== 'number') { throw TypeError('only numbers are accepted'); } if (value <= 0) throw TypeError('that is impossible'); } object[fieldName] = value; return true; } }; const proxiedStudent = new Proxy(student, studentProxy); proxiedStudent.age = 'a'; // errors proxiedStudent.age = -1; // errors proxiedStudent.age = 12; // does not error
  37. 37. Example: Value validation using Proxy const student = { firstName: 'Jackson', lastName: 'Rowe', }; const studentProxy = { set: function(object, fieldName, value) { // validation logic for the “age” field if (fieldName === 'age') { if (typeof value !== 'number') { throw TypeError('only numbers are accepted'); } if (value <= 0) throw TypeError('that is impossible'); } object[fieldName] = value; return true; } }; const proxiedStudent = new Proxy(student, studentProxy); proxiedStudent.age = 'a'; // errors proxiedStudent.age = -1; // errors proxiedStudent.age = 12; // does not error
  38. 38. Example: Protect “id” field from deletion using a Proxy const student = { id: 'jackson-rowe', firstName: 'Jackson', lastName: 'Rowe', }; const studentProxy = { deleteProperty: function(object, fieldName) { if (fieldName === 'id') return false; delete object[fieldName]; return true; } }; const proxiedStudent = new Proxy(student, studentProxy); delete proxiedStudent.id // -> false console.log(proxiedStudent.id); // jackson-rowe
  39. 39. Example: Protect “id” field from deletion using a Proxy const student = { id: 'jackson-rowe', firstName: 'Jackson', lastName: 'Rowe', }; const studentProxy = { deleteProperty: function(object, fieldName) { if (fieldName === 'id') return false; delete object[fieldName]; return true; } }; const proxiedStudent = new Proxy(student, studentProxy); delete proxiedStudent.id // -> false console.log(proxiedStudent.id); // jackson-rowe
  40. 40. Example: Protect “id” field from deletion using a Proxy const student = { id: 'jackson-rowe', firstName: 'Jackson', lastName: 'Rowe', }; const studentProxy = { deleteProperty: function(object, fieldName) { if (fieldName === 'id') return false; delete object[fieldName]; return true; } }; const proxiedStudent = new Proxy(student, studentProxy); delete proxiedStudent.id // -> false console.log(proxiedStudent.id); // jackson-rowe
  41. 41. Example: Protect “id” field from deletion using a Proxy const student = { id: 'jackson-rowe', firstName: 'Jackson', lastName: 'Rowe', }; const studentProxy = { deleteProperty: function(object, fieldName) { if (fieldName === 'id') return false; delete object[fieldName]; return true; } }; const proxiedStudent = new Proxy(student, studentProxy); delete proxiedStudent.id // -> false console.log(proxiedStudent.id); // jackson-rowe
  42. 42. Reflect JavaScript’s new built-in object
  43. 43. Reflect is all about “Reflection through introspection” - provides API to discover very low level information about code. source: keithcirkel.co.uk
  44. 44. What can be done using Reflect ➔ Call a function using Reflect.apply() ➔ Define a property using Reflect.defineProperty() ➔ Delete a property using Control over Reflect.deleteProperty() ➔ Get a property value using Reflect.get() ➔ Set property value using Reflect.set() ➔ Check if a property exist using Reflect.has() Check MDN for a list of Reflect features
  45. 45. What Reflect object offers are either a newer versions of existing methods or entirely new methods - allowing new levels of Reflection within JavaScript. source: keithcirkel.co.uk
  46. 46. Example: Deleting a property using Reflect.deleteProperty() const student = { firstName: 'Jackson', lastName: 'Rowe', }; console.log(student.firstName); // -> Jackson Reflect.deleteProperty(student, 'firstName'); console.log(student.firstName); // -> undefined
  47. 47. Example: Deleting a property using Reflect.deleteProperty() const student = { firstName: 'Jackson', lastName: 'Rowe', }; console.log(student.firstName); // -> Jackson Reflect.deleteProperty(student, 'firstName'); console.log(student.firstName); // -> undefined
  48. 48. Example: Deleting a property using Reflect.deleteProperty() const student = { firstName: 'Jackson', lastName: 'Rowe', }; console.log(student.firstName); // -> Jackson Reflect.deleteProperty(student, 'firstName'); console.log(student.firstName); // -> undefined
  49. 49. Example: Deleting a property using Reflect.deleteProperty() const student = { firstName: 'Jackson', lastName: 'Rowe', }; console.log(student.firstName); // -> Jackson Reflect.deleteProperty(student, 'firstName'); console.log(student.firstName); // -> undefined
  50. 50. 100%Modern Browsers (Edge, Firefox, Safari, Chrome) 0%IE 11 100%Node 8 source: http://kangax.github.io/compat-table/es6/ Native support
  51. 51. Resources ● Wikipedia - Metaprogramming ● Wikipedia - Reflection in Computer Science ● Mozilla - Metaprogramming ● Metaprogramming in ES6 by Keith Cirkel
  52. 52. Credits Special thanks to all the people who made and released these awesome resources for free: ▷ MDN web docs ▷ Wikipedia and the contributors ▷ Keith Cirkel for the Metaprogramming series ▷ Presentation template by SlidesCarnival
  53. 53. Thanks! Any questions? You can find me at: ➔ @mehdivk ➔ linkedin.com/in/valikhani/ ➔ hi@mv.id.au

×