Table of contents
    Properties of objects
    Types of Properties
    Defining attributes manually
    Proxy
    Metaprogramming

    Metaprogramming

    Notes while reading the metaprogramming chapter of Javascript:The definitive guide
    .08 May, 2025

    Metaprogramming - Usually we write code that fiddles with data (strings, numbers, booleans etc), which is called programming. Metaprogramming is when we write code that fiddles with other code.

    Properties of objects

    Javascript objects have properties. Each property has a value attribute.

    Let's take a simple javascript object: {a: 1, b:2}. This object has two properties. Property a of this object has value attribute equal to 1.

    But, actually, properties of javascript objects have three more attributes:

    • writable - this attribute specifies whether value of property can be set.
    • enumerable - this specifies whether property name comes in when we loop the properties of the object using for/in loop.
    • configurable - this attribute specifies whether delete object[name of the property] is allowed and whether the attributes themselves can be modified.

    The code example below demonstrates the above concept clearly:

    let obj = {x: 1}
    
    const propertyDescriptor = Object.getOwnPropertyDescriptor(obj, "x")
    console.log(propertyDescriptor)
    

    The above code snippet shows that property x of obj is writable, enumerable and configurable.

    Types of Properties

    Properties are themselves of two types. What we've talked about until now are data properties. The other type is called accessor properties. These are properties that are defined using setters and getters.

    Just like data properties have four attributes as explained above, these too have four attributes viz. get, set, enumerable and configurable.

    let obj1 = {
      x: 1,
      get y() {return this.x*2}
      
    }
    
    console.log(obj1.x, obj1.y)
    
    console.log(Object.getOwnPropertyDescriptor(obj1, "y"))
    

    obj1 in above code snippet has two properties x and y. Property x is data property while y is a accessor property. The output of above code shows the values of attributes of these properties. Note that set attribute of y is undefined cause we haven't defined a setter. We can do that like so:

    
    let obj1 = {
      x: 1,
      get y() {return this.x*2},
      set y(value) {this.x = value}
    }
    console.log(obj1.x, obj1.y)
    obj1.y = 2
    console.log(obj1.x, obj1.y)
    
    

    Accessor properties are just used to set and get values of data properties (and perform some other operation while doing so).

    Defining attributes manually

    We can have fine-grained control over the attributes of properties using Object.defineProperty. Let's use that to see what happens when turn those attributes on or off.

    
    let obj = {}
    
    Object.defineProperty(obj, "x", {
      value: 1,
      writable: false,
      enumerable: true,
      configurable: true
    })
    
    // testing writable attribute
    obj.x = 2
    console.log(obj.x)
    
    //testing enumerable attribute
    console.log(Object.keys(obj))
    
    // testing configurable attribute
    delete obj.x
    console.log(obj.x)
    
    

    Try fiddling with the attributes above and seeing the output of the tests to get a clear idea of how the value of these attributes alter the behavior of objects.

    Proxy

    Proxies can be set up on an object to override their default functioning.

    
    const props = {a:1, b:2}
    
    const proxy = new Proxy(
      props,
        {
          get(_, prop) {
            console.log(prop)
          },
        }
    )
    
    console.log(proxy.b)
    
    
    Abhimanyu