Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JavaScript Behind the Scenes: Meta Programming

JavaScript Behind the Scenes: Meta Programming

Infinite sequences, lazy properties and changing your program’s structure in runtime. Yes, JavaScript is that powerful. In this talk, I demonstrate how to solve problems in a smarter way and with better performance and readability by redefining how the language’s features work behind the scenes.

Lucas Fernandes da Costa

March 02, 2017
Tweet

More Decks by Lucas Fernandes da Costa

Other Decks in Programming

Transcript

  1. JAVASCRIPT BEHIND THE SCENES
    META PROGRAMMING
    LUCASFCOSTA LFERNANDESCOSTA

    View Slide

  2. WHAT IS META
    PROGRAMMING?

    View Slide

  3. META PROGRAMMING IS
    A PROGRAMMING
    TECHNIQUE IN WHICH
    PROGRAMS CAN TREAT
    PROGRAMS AS INPUT

    View Slide

  4. View Slide

  5. This is Meta Programming

    View Slide

  6. But this is not exactly what we will be talking about
    This is Meta Programming

    View Slide

  7. Meta Level
    JavaScript
    But this is not exactly what we will be talking about
    This is Meta Programming

    View Slide

  8. Meta Level
    JavaScript
    Base Level
    Java
    But this is not exactly what we will be talking about
    But this is not exactly what we will be talking about
    This is Meta Programming

    View Slide

  9. Reflective Meta
    Programming

    View Slide

  10. Reflective Meta
    Programming
    Introspection

    View Slide

  11. Reflective Meta
    Programming
    Introspection
    Self-modification

    View Slide

  12. Reflective Meta
    Programming
    Introspection
    Self-modification
    Intercession

    View Slide

  13. Meta Object Protocol

    View Slide

  14. Meta Object Protocol
    Provides a vocabulary to
    access and manipulate
    the structure and behavior
    of systems of objects
    https://en.wikipedia.org/wiki/Metaobject

    View Slide

  15. Meta Object Protocol
    ecma-international.org/ecma-262

    View Slide

  16. Meta Object Protocol
    ecma-international.org/ecma-262

    View Slide

  17. Meta Object Protocol
    ecma-international.org/ecma-262

    View Slide

  18. Object.*
    METHODS

    View Slide

  19. Object.* Methods

    View Slide

  20. Object.* Methods
    Object.keys()

    View Slide

  21. Object.* Methods
    Object.keys()
    Object.assign()

    View Slide

  22. Object.* Methods
    Object.keys()
    Object.assign()
    Object.hasOwnProperty()

    View Slide

  23. Object.* Methods
    Object.keys()
    Object.assign()
    Object.hasOwnProperty()
    Object.getPrototypeOf()

    View Slide

  24. PROPERTY
    DESCRIPTORS

    View Slide

  25. Property Descriptors
    Objects that
    describe properties’
    characteristics

    View Slide

  26. Property Descriptors
    myObject
    myProperty

    View Slide

  27. Property Descriptors
    myObject
    value enumerable writable configurable get set
    any boolean boolean boolean function function
    myProperty

    View Slide

  28. Property Descriptors
    myObject
    value enumerable writable configurable get set
    any boolean boolean boolean function function
    myProperty This is a Property
    Descriptor

    View Slide

  29. Indicates the property’s value
    value enumerable writable configurable get set
    any boolean boolean boolean function function
    Property Descriptors

    View Slide

  30. Indicates if this property shows
    up when enumerating properties
    value enumerable writable configurable get set
    any boolean boolean boolean function function
    Property Descriptors

    View Slide

  31. Indicates if this property’s value can be
    changed using the assignment operator
    value enumerable writable configurable get set
    any boolean boolean boolean function function
    Property Descriptors

    View Slide

  32. Indicates if this property’s
    descriptor can be changed
    and if this property can be deleted
    value enumerable writable configurable get set
    any boolean boolean boolean function function
    Property Descriptors

    View Slide

  33. The function which will be invoked
    when accessing the property
    value enumerable writable configurable get set
    any boolean boolean boolean function function
    Property Descriptors

    View Slide

  34. The function which will be called
    when trying to assign to this property
    value enumerable writable configurable get set
    any boolean boolean boolean function function
    Property Descriptors

    View Slide

  35. value enumerable writable configurable get set
    any boolean boolean boolean function function
    Property Descriptors

    View Slide

  36. Property Descriptors
    Object.defineProperty

    View Slide

  37. Property Descriptors
    Object.defineProperty
    Parameters

    View Slide

  38. Property Descriptors
    Object.defineProperty
    obj
    Parameters

    View Slide

  39. Property Descriptors
    Object.defineProperty
    obj propName
    Parameters

    View Slide

  40. Property Descriptors
    Object.defineProperty
    obj propName descriptor
    Parameters

    View Slide

  41. Property Descriptors
    Object.getOwnPropertyDescriptor

    View Slide

  42. Property Descriptors
    Object.getOwnPropertyDescriptor
    Parameters

    View Slide

  43. Property Descriptors
    Object.getOwnPropertyDescriptor
    obj
    Parameters

    View Slide

  44. Property Descriptors
    Object.getOwnPropertyDescriptor
    obj propName
    Parameters

    View Slide

  45. Property Descriptors
    Object.getOwnPropertyDescriptor
    obj propName
    Parameters
    Does not work for inherited properties!

    View Slide

  46. Property Descriptors
    Object.getOwnPropertyDescriptor
    obj propName
    Parameters
    Use Object.getPrototypeOf to go up the chain

    View Slide

  47. Getters
    Lazy Properties

    View Slide

  48. Getters
    Lazy Properties
    Properties calculated
    only when needed

    View Slide

  49. Getters
    Lazy Properties
    basket
    fruits
    {bananas: 1, oranges: 1}

    View Slide

  50. Getters
    Lazy Properties
    basket
    dataNasc
    value: 08/05/1995
    weight
    400
    fruits
    {bananas: 1, oranges: 1}

    View Slide

  51. Getters
    Lazy Properties
    basket
    dataNasc
    value: 08/05/1995
    weight
    600
    fruits
    {bananas: 2, oranges: 1}

    View Slide

  52. Getters
    Lazy Properties
    basket
    dataNasc
    value: 08/05/1995
    weight
    800
    fruits
    {bananas: 3, oranges: 1}

    View Slide

  53. Getters
    Lazy Properties
    basket
    dataNasc
    value: 08/05/1995
    weight
    800
    fruits
    {bananas: 3, oranges: 1}
    Too many assignments to
    the weight property!

    View Slide

  54. Getters
    Lazy Properties
    basket
    dataNasc
    value: 08/05/1995
    weight
    fruits
    {bananas: 1, oranges: 1}
    get: function

    View Slide

  55. Getters
    Lazy Properties
    pessoa
    dataNasc
    value: 08/05/1995
    idade
    get: function
    basket
    weight
    fruits
    {bananas: 1, oranges: 1}
    get: function
    basket.weight

    View Slide

  56. Getters
    Lazy Properties
    pessoa
    dataNasc
    value: 08/05/1995
    idade
    get: function
    pessoa
    dataNasc
    value: 08/05/1995
    idade
    get: function
    basket
    weight
    fruits
    {bananas: 1, oranges: 1}
    get: function
    calls the get function
    basket.weight

    View Slide

  57. Getters
    Lazy Properties
    pessoa
    dataNasc
    value: 08/05/1995
    idade
    get: function
    200
    pessoa
    dataNasc
    value: 08/05/1995
    idade
    get: function
    pessoa
    dataNasc
    value: 08/05/1995
    basket.weight
    idade
    get: function
    basket
    weight
    fruits
    {bananas: 1, oranges: 1}
    get: function
    calls the get function

    View Slide

  58. Getters
    Lazy Properties
    pessoa
    dataNasc
    value: 08/05/1995
    idade
    get: function
    300
    pessoa
    dataNasc
    value: 08/05/1995
    idade
    get: function
    pessoa
    dataNasc
    value: 08/05/1995
    basket.weight
    idade
    get: function
    basket
    weight
    fruits
    {bananas: 2, oranges: 1}
    get: function
    calls the get function

    View Slide

  59. Getters
    Fluid APIs
    expect('word').to.be.a('string');
    expect([1, 2, 3]).to.have.length(3);

    View Slide

  60. Getters
    Fluid APIs
    expect('word').to.be.a('string');
    Wraps expect's
    arguments into an
    Assertion object

    View Slide

  61. Getters
    Fluid APIs
    expect('word').to.be.a('string');
    These properties have
    getter functions that just
    return the Assertion itself

    View Slide

  62. Getters
    Fluid APIs
    expect('word').to.be.a('string');
    This is a method in the
    Assertion object which
    actually does the check

    View Slide

  63. Setters
    Multiple Assignments
    person
    fullName
    set: function
    firstName
    value: ''
    lastName
    value: ''

    View Slide

  64. Setters
    person
    fullName
    set: function
    firstName
    value: ''
    lastName
    value: ''
    Multiple Assignments
    person.fullName =
    'John Doe'

    View Slide

  65. Setters
    person
    fullName
    Calls the set
    function passing
    'John Doe' to it
    set: function
    firstName
    value: ''
    lastName
    value: ''
    person.fullName =
    'John Doe'
    Multiple Assignments

    View Slide

  66. Setters
    Multiple Assignments
    person
    fullName
    set: function
    firstName
    value: ''
    lastName
    value: ''
    person
    fullName
    set: function
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    Calls the set
    function passing
    'John Doe' to it
    person.fullName =
    'John Doe'

    View Slide

  67. Setters
    Multiple Assignments
    person
    fullName
    set: function
    firstName
    value: ''
    lastName
    value: ''
    person
    fullName
    set: function
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    Calls the set
    function passing
    'John Doe' to it
    person.fullName =
    'John Doe'

    View Slide

  68. Setters
    Multiple Assignments
    person
    fullName
    set: function
    firstName
    value: ''
    lastName
    value: ''
    Be careful not to
    set fullName again
    inside the setter
    fullName
    set: function

    View Slide

  69. Setters
    Multiple Assignments
    person
    fullName
    set: function
    firstName
    value: ''
    lastName
    value: ''
    Be careful not to
    set fullName again
    inside the setter
    fullName
    set: function
    INFINITE RECURSION!

    View Slide

  70. Setters
    Multiple Assignments
    person
    fullName
    set: function
    firstName
    value: ''
    lastName
    value: ''
    Be careful not to
    set fullName again
    inside the setter
    You can use a getter to
    concatenate firstName
    and lastName

    View Slide

  71. PROXIES

    View Slide

  72. What are Proxies?
    Proxies are object
    wrappers which allow
    us to intercept
    operations

    View Slide

  73. Proxy Wrapper
    handler
    get
    set: function
    set
    set: function
    target
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    Traps
    A PROXY’S COMPOSITION

    View Slide

  74. HOW PROXIES WORK
    Proxy Wrapper
    const p = new Proxy(target, handler);

    View Slide

  75. Proxy Wrapper
    target
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    const p = new Proxy(target, handler);
    HOW PROXIES WORK

    View Slide

  76. Proxy Wrapper
    target
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    handler
    get
    function
    set
    function
    const p = new Proxy(target, handler);
    HOW PROXIES WORK

    View Slide

  77. const p = new Proxy(target, handler);
    p
    Proxy Wrapper
    target
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    handler
    get
    function
    set
    function
    HOW PROXIES WORK

    View Slide

  78. const p = new Proxy(target, handler);
    p.name
    Asks the
    wrapper for
    this property
    Proxy Wrapper
    target
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    handler
    get
    function
    set
    function
    HOW PROXIES WORK

    View Slide

  79. handler
    get
    set
    function
    const p = new Proxy(target, handler);
    p.name
    Calls the get
    trap passing
    target and the
    property’s name
    target
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    Proxy Wrapper
    function
    HOW PROXIES WORK

    View Slide

  80. Proxy Wrapper
    handler
    get
    function
    set
    function
    const p = new Proxy(target, handler);
    p.name
    Returns the
    value returned
    by the get trap
    target
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    HOW PROXIES WORK

    View Slide

  81. List Comprehensions
    Infinite
    Arrays

    View Slide

  82. 0 1 2
    0 2 4
    evens[0]
    Proxy Wrapper
    get
    function
    Infinite
    Arrays

    View Slide

  83. 0 1 2
    0 2 4
    evens[0]
    Proxy Wrapper
    get
    function
    Infinite
    Arrays

    View Slide

  84. 0 1 2
    0 2 4
    evens[0]
    Proxy Wrapper
    get
    function
    Infinite
    Arrays

    View Slide

  85. 0 1 2
    0 2 4
    evens[0]
    Proxy Wrapper
    get
    function
    Infinite
    Arrays

    View Slide

  86. 0 1 2
    0 2 4
    evens[0]
    Proxy Wrapper
    get
    function
    Infinite
    Arrays

    View Slide

  87. 0 1 2 3
    0 2 4 undefined
    evens[3]
    Proxy Wrapper
    get
    function
    Infinite
    Arrays

    View Slide

  88. 0 1 2 3
    0 2 4 undefined
    evens[3]
    Proxy Wrapper
    get
    function
    Infinite
    Arrays

    View Slide

  89. 0 1 2 3
    0 2 4 undefined
    evens[3]
    Infinite
    Arrays
    Proxy Wrapper
    get
    function

    View Slide

  90. 0 1 2 3
    0 2 4 undefined
    evens[3]
    Proxy Wrapper
    get
    function
    Calculates the value that should
    have been in the index 3
    Infinite
    Arrays

    View Slide

  91. 0 1 2 3
    0 2 4 undefined
    evens[3]
    Proxy Wrapper
    get
    function
    Calculates the value that should
    have been in the index 3
    Infinite
    Arrays

    View Slide

  92. Infinite
    Arrays

    View Slide

  93. Property
    Suggestions
    Helps your API's
    users figure out what
    is the correct way to
    use it

    View Slide

  94. person
    target
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    handler
    get
    function
    set
    function
    Property
    Suggestions

    View Slide

  95. Property
    Suggestions
    person
    target
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    handler
    get
    function
    set
    function
    typo!
    person.firstNsme

    View Slide

  96. person
    target
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    handler
    get
    function
    set
    function
    Property
    Suggestions
    person.firstNsme

    View Slide

  97. person
    target
    firstName
    value: ‘John'
    lastName
    value: ‘Doe'
    handler
    get
    function
    set
    function
    person.firstNsme
    Property
    Suggestions

    View Slide

  98. person
    target
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    handler
    get
    function
    set
    function
    pessoa.seuNome
    Perhaps you meant:
    "firstName"?
    Property
    Suggestions

    View Slide

  99. Property
    Suggestions
    expect({}).to.be.a.strng
    TYPO!

    View Slide

  100. Property
    Suggestions
    expect({}).to.be.a.strng
    Trying to access a
    non-existing property
    returns undefined

    View Slide

  101. Property
    Suggestions
    expect({}).to.be.a.strng
    assertion
    target
    handler
    get
    function
    set
    function
    Checks if
    property
    assertion
    exists here

    View Slide

  102. Property
    Suggestions
    expect({}).to.be.a.strng
    assertion
    target
    handler
    get
    function
    set
    function
    Checks if
    property
    assertion
    exists here
    This does not exist
    Perhaps you meant: 'string'

    View Slide

  103. “Reflecting" Operations
    ES6 Reflect

    View Slide

  104. ES6 Reflect
    Reflect.get
    “Reflecting" Operations

    View Slide

  105. ES6 Reflect
    Reflect.get
    Reflect.set
    “Reflecting" Operations

    View Slide

  106. ES6 Reflect
    Reflect.get
    Reflect.set
    Reflect.[[trapName]]
    “Reflecting" Operations

    View Slide

  107. const p = new Proxy(obj, {
    get: (target, propName) => {
    console.log('[GET] ' + propName);
    return Reflect.get(target, propName);

    }
    });
    “Reflecting" Operations

    View Slide

  108. const p = new Proxy(obj, {
    get: (target, propName) => {
    console.log('[GET] ' + propName);
    return Reflect.get(target, propName);

    }
    });
    “Reflecting" Operations
    Trap Name == Reflect Method’s Name

    View Slide

  109. SYMBOLS

    View Slide

  110. What are symbols?
    A new primitive type

    View Slide

  111. "Symbols are all about Reflection
    within implementation"
    Keith Cirkel
    What are symbols?
    A new primitive type

    View Slide

  112. symbol Symbol
    !==

    View Slide

  113. symbol Symbol
    !==
    Primitive Type

    View Slide

  114. symbol Symbol
    !==
    Primitive Type Object Wrapper

    View Slide

  115. symbols
    new Symbol('description')

    View Slide

  116. symbols
    new Symbol('description')

    View Slide

  117. symbols
    new Symbol('description')
    the new keyword
    denotes the creation
    of a new object

    View Slide

  118. symbols
    new Symbol('description')
    the new keyword
    denotes the creation
    of a new object
    returns a symbol
    (primitive)
    Symbol('description')

    View Slide

  119. symbols
    new Symbol('description')
    Symbol('description')
    the new keyword
    denotes the creation
    of a new object
    returns a symbol
    (primitive)

    View Slide

  120. PRIMITIVE
    SYMBOLS ARE
    UNIQUE

    View Slide

  121. A symbol is unique
    Symbol('id') !== Symbol('id')

    View Slide

  122. Symbol('id') !== Symbol('id')
    A symbol is unique
    Symbol.for('id') === Symbol.for('id')
    Be careful with the global registry

    View Slide

  123. Symbol('id') !== Symbol('id')
    Symbol.for('id') === Symbol.for('id')
    Be careful with the global registry
    A symbol is unique
    These two return the exact same primitive

    View Slide

  124. symbols in the real world
    person
    firstName
    value: 'John'
    lastName
    value: 'Doe'

    View Slide

  125. markTimestamp(person)
    symbols in the real world
    person
    firstName
    value: 'John'
    lastName
    value: 'Doe'

    View Slide

  126. markTimestamp(person)
    person
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    timestamp
    value: 1481236691
    symbols in the real world
    person
    firstName
    value: 'John'
    lastName
    value: 'Doe'

    View Slide

  127. Easily overwritten by
    mistake/namespace clash
    symbols in the real world
    markTimestamp(person)
    person
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    timestamp
    value: 1481236691
    person
    firstName
    value: 'John'
    lastName
    value: 'Doe'

    View Slide

  128. pessoa
    nome
    value: 'João'
    sobrenome
    value: 'Silva'
    person
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    symbols in the real world

    View Slide

  129. symbols in the real world
    markTimestamp(person)
    person
    firstName
    value: 'John'
    lastName
    value: 'Doe'

    View Slide

  130. person
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    symbol(time)
    value: 1481236691
    symbols in the real world
    markTimestamp(person)
    person
    firstName
    value: 'John'
    lastName
    value: 'Doe'

    View Slide

  131. We will never have an
    unintentional assignment
    person
    firstName
    value: 'John'
    lastName
    value: 'Doe'
    symbol(time)
    value: 1481236691
    symbols in the real world
    markTimestamp(person)
    person
    firstName
    value: 'John'
    lastName
    value: 'Doe'

    View Slide

  132. Symbols used to drive
    the language’s native
    implementations
    Well Known Symbols

    View Slide

  133. Indexes the function used to
    check if an object is an instance
    of a constructor
    Well Known Symbols
    Symbol.hasInstance

    View Slide

  134. Well Known Symbols
    Symbol.hasInstance
    bristol instanceof AwesomePlace

    View Slide

  135. Well Known Symbols
    Symbol.hasInstance
    AwesomePlace[Symbol.hasInstance](bristol)
    bristol instanceof AwesomePlace

    View Slide

  136. Well Known Symbols
    Symbol.hasInstance

    View Slide

  137. Well Known Symbols

    View Slide

  138. Well Known Symbols
    Symbol.toPrimitive

    View Slide

  139. Well Known Symbols
    Symbol.toPrimitive
    Symbol.toStringTag

    View Slide

  140. Well Known Symbols
    Symbol.toPrimitive
    Symbol.toStringTag
    Symbol.iterator

    View Slide

  141. Well Known Symbols
    Symbol.toPrimitive
    Symbol.toStringTag
    Symbol.iterator
    And many others…

    View Slide

  142. THANK YOU!
    MORE DETAILS AT LUCASFCOSTA.COM
    LUCASFCOSTA LFERNANDESCOSTA

    View Slide