JS Value Objects – 3. Integer data type

Note: This is the third post in a series about the use of Value Objects in JavaScript.

In the previous posts we introduced ourselfs to the concept of the Value Object design pattern and the way JavaScript implements data types (or value types) In this post we will start building our own value object in JS: the integer.

Reverse engeneering

Now let’s try if we can create an object that behaves the same way as the String object we inspected in the previous post.

We create a constructor called MyString, accepting a value, and give it the method valueOf in its prototype. The valueOf method is in fact overwriting the default one defined in Object.prototype, which all objects inherit from. This method is called whenever JavaScript tries to perform a binary operation on an object. For example when comparing (==) or joining (+) strings. Now lets run the same tests again on this new object:

So far, so good. We have similar results for these tests. But let’s be honest, we did not recreate the String object and that’s also not what we want to achieve. What we did do, is getting to know how you might create your own data types, or Value Objects.

Integer data type

An interesting thing about JavaScript is that it does not have an Integer data type. It has Number, representing all numerical values, but if we want to be sure that there is no fractional component we need to check it whenever we need to. To make our lives easier we will create our own Integer object instead:

This Value Object embodies the new data type: Integer. It can not be instantiated with an invalid value (we still want the value to be a number) and it can, because of the valueOf method, still be used in calculations:

When you instantiate a new String, Boolean, Number, etc without passing an argument, the object will get a default value (respectively “”, false, 0). This might not always be necessary, but lets say in our case it is preferable.

Note: To check if the value was passed you might be used to check if the value is falsable (but of course the passed value might be 0) or check if value is strictly equal to undefined, but that would mean you can not instantiate an Integer with undefined. I don’t know if you would be needing to, but still, this is the most valid way to check if really no argument was passed.

Parsing input

Another thing noticeable is that all native data type objects will never fail on no matter what input you would pass them.

String will always try to cast the input to a string which, in the worst case, will lead to “[object Object]”-like values, a Number will use the value NaN (Not a Number) whenever it can’t handle the input, a Boolean can cast all types of input, because if it is not falseable it is true and an Array will take its arguments as items in its list (just like the literal version []).

The Object-object treats its input with even more care. When you pass a string as a value it will create a string for you and returns the same result as you would get when creating a string with the String-object. The same counts for Number, Boolean, Array and even RegExp. In fact, if you pass it an object it will just return it back untouched.

In our case we would also like to be able to pass a value as a string. Maybe not as part of a sentence (like you would do with parseInt), but a value like “3” or “-12” should be possible. Beside that, it would also be nice if it would round a float to an integer.

When we multiply the value by one, JavaScript will first try to cast the value to a number and then multiply it by 1, resulting in either a valid number or NaN. When trying to round NaN to the nearest whole number the value will stay NaN, which will trow an exception on validation.

Immutability

But wait! We can still change the value of the object!

We said in the first post that values have to be immutable. Values do not change, they simply get assigned to variables and removed if another value is assigned. Luckily JS provides us a way to assign properties in a way that they can not be changed.

Methods

Objects can have methods. Those functions can help us to interact with the value. The String object for instance has a method called toUppercase which will return a new all uppercase string of the original value. It is important to understand that this produces a new string and does not change the original string, because value objects are immutable.

In our case we might want to have a toFixed method that will produce a string created from the original value with a fixed number of decimals. Methods can be created on the prototype object, just like the valueOf function.

This will let us do whatever we want, but we have to keep ourselfs in control by making sure we doe not change the value itself.

The method in this example already exists on the Number object with exactly the same purpose. An integer and number (float) are much alike actaully, so why dont we just copy all methods in the Number prototype to the Integer prototype? Well, we cannot just copy paste as-is because the methods probably expect a certain internal structure and behaviour that is different in the Integer object. Instead we have to call the methods on the actual value by wrapping them into function.

Summary

Hurray! We created our first value object! By taking the Integer as an example we saw how a constructor can be used and tweaked to create an object that can pass as a value object. An object that is immutable and, as long as its value is not an object itself, cannot be referenced.

There is only one important part missing: The life cycle. Integers with the same value are not equal to each other, because both are objects and objects are unique.

In the next post we will take a closer look to this problem and I will write some more practical examples.

Leave a Reply

Your email address will not be published. Required fields are marked *