Object Funsies
Objects are a fun data structure in javascript. Well, I guess if your definition of “fun” is creating a dictionary and then accessing that dictionary every time you need to know something.
Fun, right?
You bet your ass it’s fun.
There was a time when I thought that objects were just these simple data structures that held two things: a key and a value.
var object = {
key: 'value'
};
It was great! I knew that if I needed a hash map, a dictionary, or a structure that I’d be using to look stuff up, I’d use an object. They were so simple! So brilliant!
They’re still brilliant, but over time I found out that they could get as complex as you wanted them. The object primitive, {}, is what most javascripters use when they need an object. There’s also Object.create
Some Object.create magic
var object = {
key: 'value'
};
var obj = Object.create(null);
The difference between these two is that the former has a prototype, which is Object. The latter, however, has no prototype. Object.create accepts a parameter that can either be an object, or null, and it uses that object to define the created object’s prototype (instead of the default Object prototype). If it’s null, the object is literally created with no prototype, meaning it’s a truly naked POJO (plain ol’ javascript object).
Object creation became more interesting to me at that point. Amazing that an object could be created completely clean and free of prototypes.
It gets even better. Objects can have a prototype that’s completely configurable.
Configurable properties with Object.create
var object = {
key: 'value'
};
var obj = Object.create({
key: {
value: 'value',
writable: true,
enumerable: false,
configurable: true
}
});
Yeah. That’s right. we’re gettin’ REAL object heavy right now, but let’s take a look at the flexibility this affords us by deconstructing that Object.create call up there.
var obj = Object.create({
key: {
value: 'value', // The value for this key
writable: true, // Determines whether or not we can modify the value
enumerable: false, // Determines whether or not this shows up in for loops
configurable: true // Can we change (configure) these 'meta' properties?
}
});
So it turns out that with any property defined on an object this way, we can define whether or not it can change, whether or not it can be looped over, as well as whether or not we can actually change these meta-level properties at a later time!
Objects are pretty nuts. There’s another interesting thing conferred by using Object.create, it’s custom getting and setting.
Custom getting and setting with Object.defineProperty
function Box() {
var _value = 5;
// Add a 'value' property, making this.value retrievable and accessible
Object.defineProperty(this, 'value', {
get: function() {
console.log('THERE CAN BE ONLY ONE HIGHLANDER');
// We're able to access this via a closure, so we can return it!
return _value;
},
set: function(val) {
console.log('NEW VALUE OH BOY');
// val in this case is the right hand side of the
// assignment expression
_value = val;
}
});
}
var crate = new Box();
I’m using Object.defineProperty here to attach a property to the this of the Box() constructor. This is ridiculous. But you see where I’m going with this, right? You could create any object property and define custom getters and setters on it that could potentially produce all kinds of side effects.
For example, based on my definition up there, literally any time I access crate.value, that is, any time I read crate.value, the get function runs, and I get ‘THERE CAN BE ONLY ONE HIGHLANDER’ output to my console.
Every time.
Conversely, if I assign a new value to my key via crate.value = 5, I’ll get “NEW VALUE OH BOY” thrown into my console because it’ll run the set function.
This is amazing. It’s also incredibly ill-advised without a proper use-case. But the point is can you believe this is a thing?
I sure can. Because javascript is just WEIRD.
Custom serialization
Custom serialization is another thing you can do! if you need a serialized object and you don’t like what JSON.stringify(object) gives you, you can just tell the object itself what it needs to do when it should be serialized.
var object = {
key: 'value',
toJSON: function() {
return "Literally nothing related to this object. Serialized";
}
}
Essentially what this object is doing is specifying a toJSON() property. Normally, when serializing an object, you end up with a string verson of the object as specified by JSON.stringify(). By defining it at the object level, the serialization process via JSON.stringify() will use that method on the object instead of the default method of serialization. While not strictly prototypal, it mimics behavior similar to the prototype chain. I covered this in the last article about how prototypal inheritance and delegation up the prototype chain works.
(Go read it.)
(Also read about the custom .toJSON() property)
Anyways, Now we have an object that will run (and serialize the return value of) a custom hook if we call it with JSON.stringify()!
var object = {
key: 'value',
toJSON: function() {
return "Literally nothing related to this object. Serialized";
}
}
JSON.stringify(object) === '"Literally nothing related to this object. Serialized"' // true
Again, This works because toJSON()exists on object. Since we’ve defined it here, it actually takes this function and uses it to output a serialized return value from toJSON()
Your brain is a little fried
This isn’t the only time javascript is gonna blow your mind. it’s ridiculous how flexible this language is, man. I recommend you head over to JSBin or some other editor and try these things out! And if none of them work, please tell me so I can actually fix my examples (if they’re not-so-hot)
Happy coding!