Did you know you can attach a key to a JavaScript object that is actually a callable?

For example:


const data = await response.json()

Object.defineProperty(data, 'magic', {
  get: () => {
    return Math.random()
  },
})

console.log({magic: data.magic})

will print:

{ magic: 0.6778944803790492 }

And suppose you want it memoized, you can use this:


const data = await response.json()

let magic
Object.defineProperty(data, 'magic', {
  get: () => {
    return magic ??= Math.random()
  },
})

console.log({magic: data.magic})
console.log({magic: data.magic})
console.log({magic: data.magic})

will print:

{ magic: 0.21367035961590308 }
{ magic: 0.21367035961590308 }
{ magic: 0.21367035961590308 }

Note that it doesn't allow setting. If you do this:


Object.defineProperty(data, 'magic', {
  get: () => {
    return Math.random())
  },
  set: () => {
    throw new Error('Nope!')
  },
})

data.magic = 42

it will print:

Error: Nope!

One thing that bit me today, and much the reason why I'm writing this, is that I had this:


async function getData() {
  const response = await get()
  const data = await response.json()

  Object.defineProperty(data, 'magic', {
    get: () => {
      return Math.random()
    },
  })

  return {...data}
}


// Example use:

const {userName, magic} = await getData()
console.log({userName, magic})

// Will print
// { userName: 'peter', magic: undefined }

This does not work because the magic property is not enumerable. To fix that, make this edit:


  Object.defineProperty(data, 'magic', {
    get: () => {
      return Math.random()
    },
+   enumerable: true,
  })

Now, the same code as above, when you run console.log({userName, magic}) it will print:

{ userName: 'peter', magic: 0.23560450431932733 }

Comments

Your email will never ever be published.

Related posts