build with purpose

Javascript Object Creation

Object creation in Javascript is dead simple from a data type perspective:

var obj = {a: 'Ben', b: 'Hofferber', c: 'Boise, ID'}
typeof obj // object

The datatype is straightforward and is really easy to implement. With JSON we can directly pass these objects into our javascript applications to be used immediately.

I’ve even gone a bit further to look at the byte size of the object using a node library object-sizeof:

const sizeof = require('object-sizeof')
sizeof(obj) // 48 (bytes)

sizeof('Ben') // 6
sizeof('Hofferber') // 18
sizeof('Boise, ID') // 18
// 6 + 18 + 18 = 42 (where is the other 6?)

It looks like the object from before takes up 48 bytes in memory to store. As long as an existing reference continues to exist to that object, it will continue to hold that memory. Once the reference is removed, it will get Garbage Collected and the memory will become freed.

Note: while object-sizeof is based off of the ECMAScript Lanuguage Specification, it does not factor in V8 optimizations which may additionally optimize code. I still use it because it provides a good insight into the inner workings of the core language.

When we start breaking down how large this object is, it doesn’t look like it adds up. It’s actually hard to see where the other memory is being stored.

sizeof({}) // 0
sizeof({a: null, b: null, c: null}) // 6

There it is!

When we specify keys to store values in the object, it has to allocate those key names to reference each value. Those keys are stored as strings which is clear from the output of Object.keys:

Object.keys(obj) // ['a', 'b', 'c']

If that’s the case, this might start to get you thinking a bit more about how these specific objects store data. For example, one function that our new object has is ‘.isPrototypeOf()’. Based on what we just saw, this function must not take up any space. Is that true?

sizeof(obj.isPrototypeOf) // 0

It isn’t true, but it sure looks that way. This is another caveat of relying on the ECMAScript spec because there is no definition for how large an object is and object-sizeof doesn’t know how to handle a function.

Object Delegation

One thing that I can say is that I bet every object shares the same underlying .isPrototypeOf() function. This is because they all share the same underlying prototype object to generically give all objects the same functionality and to save space. This type of object creation is sometimes called Object Delegation.

Let’s look at what this looks like:

// Define a generic prototype for our skiiers to inherit
const skiierPrototype = {
  name: '',
  hometown: '',
  medals: [],
  greet: function() {
    return 'Hi, I am ' + this.name
  },
  strut: function() {
    return this.medals.map(m => m.type)
  }
}

// https://en.wikipedia.org/wiki/Julia_Mancuso#Olympic_results
let julia = Object.create(skiierPrototype)
julia.name = 'Julia Mancuso'
julia.hometown = 'Reno, NV'
julia.medals.push({year: 2006, type: 'Gold'})
julia.medals.push({year: 2008, type: 'Silver'})
julia.medals.push({year: 2008, type: 'Silver'})
julia.medals.push({year: 2014, type: 'Bronze'})

// https://en.wikipedia.org/wiki/Ted_Ligety#Olympic_results
let ted = Object.create(skiierPrototype)
ted.name = 'Ted Ligety'
ted.hometown = 'Salt Lake City, UT'
ted.medals.push({year: 2006, type: 'Gold'})
ted.medals.push({year: 2008, type: '5th'})
ted.medals.push({year: 2014, type: 'Gold'})

skiierPrototype.isPrototypeOf(julia) // true
skiierPrototype.isPrototypeOf(ted) // true

julia.greet() // "Hi, I am Julia Mancuso"
ted.greet() // "Hi, I am Ted Ligety"

julia.strut()
// ["Gold", "Silver", "Silver", "Bronze", "Gold", "5th", "Gold"]
ted.strut()
// ["Gold", "Silver", "Silver", "Bronze", "Gold", "5th", "Gold"]

So now we’ve used Object.create to create some skiiers and then we’ve modified these skiiers to use some metadata relating to each individual skiier. This feels pretty similar to using just regular objects but initializing them a different way. As you can see above with the strut() method however, there are some major considerations.

It’s normal to ask: Why would we ever want to do this? What’s up with that bug down there? This looks like a horrible idea!

So, we want to do this for consistency and efficiency. Both ted and julia share the functions greet() and strut() without having to define completely new functions to make it happen. Those functions would be owned directly by each julia and ted and there would be no way to know if they each shared the same function because the functions were added individually. It helps the programmer by defining an interface to code against.

That bug with strut is a side-effect of manipulating medals directly. The medals array is created in the prototype and inherited by both ted and julia. However, when this array is directly manipulated and mutated when new medals are pushed onto it, the base prototype’s array is changed.

skiierPrototype.strut()
// ["Gold", "Silver", "Silver", "Bronze", "Gold", "5th", "Gold"]
skiierPrototype.greet()
// "Hi, I am "

This doesn’t happen with name however. That’s because we are assigning a new object to the name reference instead of modifying the object located at the existing reference.

It’s easier to accidentally modify prototype arrays and maps within objects which is why it’s usually a good idea to define immutable functions to interface with these attributes. In the code below I modify the skiierPrototype to have an addMedal() function rather than having the users modify the medals list directly.

const R = require('ramda')
const skiierPrototype = {
  ...
  addMedal: function(medal) {
    this.medals = R.append(medal, this.medals)
  }
}

...

julia.addMedal({year: 2014, type: 'Bronze'})

R.append() assigns a completely new list of medals rather than editing the original. Then julia’s reference to medals is updated rather than the base element being modified. I think that this particular process is immutable because it doesn’t change the underlying data within the object. If it was fully immutable then addMedal() would return a completely new skiier with the additional medal added.

I’m planning on looking deeper into object creation in the future, but this was a surprisingly fun dive into the world of object creation. I’m looking forward to looking more into:

  • Object Cloning
  • Object Factories
  • Static Memory Pooling of Objects
  • HTTP Object Memory Footprint

Make my day and share this post:

Other posts to peak your interest:

  • Understanding Visual Testing
  • The Redux Saga Black Box
  • What my college degree gave me
  • Technical Leaders Enabling Stronger Teams
  • Finding Your Gateway to Learning Vue
  • Why Write Server Rendered Frontend Apps
  • comments powered by Disqus
    © 2019. All rights reserved.