Bake A Javascript Library using Vanilla JS (Part 4)
Before you go ahead, this is a mini-series where we start from ground zero to a shippable Javascript Date Library 📅
View the previous article to be on the track! 😄
Let’s Go! 🚀
Previously —
We utilized Prototypal Inheritence to create a structured way to have our functions written in our library. We used the modern technique of chaining Javascript functions when invoking and made our new instances of our Time class immutable!
Next, we learn to create some hooks using Javascript Object Methods to make our library more robust! 🚀
Contents
- Options
- Object.assign()
- Freeze and Define
Options
Until now we have built a pretty good library, which can be used to as much extent as it can provide. But what if our users want the output to be in a specific way?
For example, if we use getDay()
the method from our library -
//index.js...rest of the code
let Christmas = new Time(["2021", "04", "04"]);
let day = Christmas.getDay(Christmas);console.log(day)
...rest of the code
The output would be —
Tuesday
But what if our users want an output like Mon, Tue
…. and so forth and so on?
Let’s introduce a new parameter to be passed to our Time
constructor, and also modify one of our methods getDay
to show our changes.
//Time.js...rest of the code...function Time(date, options) { if(!Array.isArray(date)) { // check if input is NOT an array
date = [...arguments] //convert the input into an array
} this.date = new Date(..date)
this.settings = options
}Time.prototype.getDay = function() {
return this.settings.days[this.date.getDay()]
}
...rest of the code...
also, we would now pass the options to our instance
//index.js
...rest of the code...let Christmas = new Time(["2021", "04", "04"], {
days: ['Sun', 'Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat'],
});let day = Christmas.getDay(Christmas);
console.log(day)...rest of the code...
Refresh the browser, and now the output would be —
What’s happening above? -
- We modified our Time
constructor
function, to accept options. - We modified our
getDay
method to make sure our options are utilized. - So now if the user passes a short form of the days in the form of an object containing the
days
key. - The
getDay
returns the corresponding Day according to the numeric day matching index on the array in short form.
Object.assign()
Since we managed to provide options for users to pass on when creating a date instance. What if we don’t pass anything now? What would happen?
Let’s test!
If you remove the options passed to our class invocation syntax.
//index.js
...rest of the code...let Christmas = new Time(["2021", "04", "04"]); //removed options.let day = Christmas.getDay(Christmas);
console.log(day)...rest of the code...
Let’s refresh the browser and check!
What’s causing this, as we know we had earlier modified the getDay
method where we returned this.settings.days[this.date.getDay()]
, where if the settings
the object doesn’t contain days
key it would throw an error because it is undefined
, hence the above error as in the screenshot.
How do we handle this? Bring in Object.assign(), as per MDN —
The
Object.assign()
method only copies enumerable and own properties from a source object to a target object. It uses[[Get]]
on the source and[[Set]]
on the target, so it will invoke getters and setters.
So, what we have to do is set some Default values to fall back on in case the user doesn’t pass any extra options explicitly.
We shall modify our Time
constructor in Time.js
file
//index.js function Time(date, options = {}) {...rest of the constructor codelet defaults = Object.assign({
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
}, options)this.settings = defaults...rest of the constructor code
}..rest of the code...
What’s happening above? -
Object.assign
takes two parameters,target
andsource
.- When the
options
are passed via invocated class object to our method, theoptions
as it is defaulted to an empty object usingoptions = {}
in our parameter definition, assigns the default values of month and days to our options object. - In case someone passes
options
it overwrites our existing default options. - NOTE that
this.settings
has value ofdefaults
. - In short
options -> let default = Object.assign([...{}], options) -> this.settings = default
Now refresh the browser —
Freeze and Define
Until now we had given options to users, and also set defaults in case they don’t pass anything. But let’s go into the fact the users can still modify our variables by dot operator.
For example —
//index.js
...rest of the code...let someDate = new Time(["2021", "04", "04"], {
days: ['Sun', 'Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat'],
});someDate.settings.day = ["intrusion"]console.log(someDate)...rest of the code...
Now if we refresh the browser and expand the someDay object you can see we have modified the settings key days
-
I mean it's very plausible they could do this, but since we are building a library we want to be as the industry standard and robust as possible. We don't want users to mess with our internal code simply by just doing such shenanigans.
Of course, the above example looks harmless, but what if its Password Hashing function and one of our core variables are accessible like this?
Call in the big guns! Object.freeze
and Object.defineProperties
.
- Object.freeze()
According to MDN Web Docs —
The
Object.freeze()
method freezes an object. Freezing an object prevents extensions and makes existing properties non-writable and non-configurable. A frozen object can no longer be changed: new properties cannot be added, existing properties cannot be removed, their enumerability, configurability, writability, or value cannot be changed, and the object's prototype cannot be re-assigned.
As the definition above we shall freeze our constructor and its properties so people cannot explicitly modify it according to their will!
//Time.jsfunction Time(date, options = {}) {...rest of the constructor code Object.freeze(settings) // Freeze em....rest of the constructor code}..rest of the code...
Refresh the browser and you can no longer modify our settings!
2. Object.defineProperties()
As per the MDN Web Docs —
The
Object.defineProperties()
method defines new or modifies existing properties directly on an object, returning the object.
To simplify the above definition, we are basically structuring or tightening the rules of our variables in our constructor. Giving it a strong definition in terms of object and its properties accessibility.
Let’s code it —
//Time.jsfunction Time(date, options = {}) {...rest of the constructor code/*remove this line*/
this.date = new Date(...date)
/*remove this line*/Object.freeze(settings) // Freeze em.Object.defineProperties(this, { //define them
date : {
value: new Date(...date),
},
settings: {
value: settings,
}
})/*remove this line*/
this.settings = defaults
/*remove this line*/...rest of the constructor code}..rest of the code...
What’s happening above? -
Object.defineProperties
takes two parametersobj
&props
- Parameter
obj —
The object on which to define or modify properties. - Parameter
props —
An object whose keys represent the names of properties to be defined or modified and whose values are objects describing those properties - As you can see we have removed the variable assigning syntax for
this.date
&this.settings
reason being we are modifying its value viaObject.defineProperties
, pointing to the context ofthis
which is passed asobj (first param)
.
Finally, your time constructor should look something like this —
This concludes Part 4, if you have any doubts or improvements for this article please do comment, and if you liked it share a few applause 😄.
In Part 5, we shall move towards finally thinking in terms of the most awaited part Shipping! 🛳 So get your sails ready, it's gonna be one great adventure, as we are nearing the conclusion of our entire journey, or are we?
P.S: Part 5 would be released sometime this week, subscribe to stay updated.
****************************************************************
This post was inspired by — https://gomakethings.com/ , please do check out this wonderful blog based on pure Javascript.
I’m Saurav Tiru, Front End Engineer at building UI at Radius.
I would be posting articles related to Front End Engineering, Photography, and Video Editing every week!