Bake A Javascript Library using Vanilla JS (Part 4)

Saurav Tiru
7 min readAug 10, 2022

--

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! 🚀

Anne Hathway’s method of eating muffins is top-notch.

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

  1. Options
  2. Object.assign()
  3. Freeze and Define

Options

Having a lot of options help, don't they?

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 Timeconstructor 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()

Make em fluffy!

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!

Oops!

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 and source.
  • When the options are passed via invocated class object to our method, the options as it is defaulted to an empty object using options = {} 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 of defaults.
  • In short options -> let default = Object.assign([...{}], options) -> this.settings = default

Now refresh the browser —

Error’s gone!

Freeze and Define

Frosting we love frosting

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 .

  1. 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!

Thou shall not modify!

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 parameters obj & 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 via Object.defineProperties , pointing to the context of this which is passed as obj (first param) .

Finally, your time constructor should look something like this —

The image might differ from your actual code but would work the same if you followed the instructions.

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!

--

--