We have been building up our Color Converter app in the previous discussions, and so far, we have discussed Prototypes, Factory Functions, and Constructor Functions. We have seen how these processes work and while using a Constructor Function greatly improved our code efficiency over our Factory Function, we can improve our code even more by using Classes.
As a refresher, here is the Constructor Function code:
- JavaScript
-
- function MakeColor ( r = 190, g = 155, b = 90 ) {
- this.r = r;
- this.g = g;
- this.b = b;
- };
-
- MakeColor.prototype.rgb = function() {
- const { r, g, b } = this;
- return `rgb(${r}, ${g}, ${b})`;
- };
-
- MakeColor.prototype.rgba = function( a = 1.0 ) {
- const { r, g, b } = this;
- return `rgba(${r}, ${g}, ${b}, ${a})`;
- };
-
- MakeColor.prototype.hex = function() {
- const { r, g, b } = this;
- return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
- };
-
- const newColor = new MakeColor();
-
- console.log(`newColor.rgb = ${newColor.rgb()}`);
- console.log(`newColor.rgba = ${newColor.rgba(0.5)}`);
- console.log(`newColor.hex = ${newColor.hex()}`);
While this is a much better approach over our Factory Function, We can improve the coding even more by creating a Class.
Creating a Class
One of the biggest advantages of using a Class structure over a Constructor Function is that any methods that we create will be contained within the Class structure itself, and not externally to the Class function.
Let's start be defining our new Class. You start by designating the keyword class followed by the Capitalized Name of the class:
The next step is to add a Constructor Function to our class. This must be the first designation after creating our new class and is required. Also note, the keyword name of constructor can not be changed.
The constructor will run immediately when this class is called and it works exactly like our previous "constructor function" we created in the Constructor Functions discussion. We pass in our variables and assign them to this, like so:
- class MakeColor {
- constructor ( r = 215, g = 170, b = 80, name = 'Bronze' ) {
- this.r = r;
- this.g = g;
- this.b = b;
- this.name = name;
- }
- }
You may notice something new here. We've added an additional variable called name, to our constructor. This is optional but gives us the ability to "name" the color we are defining, such as "Red", "Coral", "Bronze", or whatever you like.
So now all of our class Variables have been defined within our class constructor, and if you call this class like so:
- const color1 = new MakeColor();
- console.log(color1);
and look at the console log you will see that we have indeed created an object, and that inside of the [[prototype]] for our object, we have our constructor.
Adding the Methods
So we now have our basic class built, along with it's Constructor Function and all is working properly.
So let's start adding our methods to our class. Here is where using a Class is better than using a Constructor Function:
- Our methods are contained within the Class structure itself, so everything is more organized.
- When defining our method, we do not use the [[prototype]] designation (constructorFunctionName.prototype.methodName)
Add a method:
- rgb() {
- const { r, g, b, name } = this;
- return `${name} = rgb(${this.innerRGB()})`;
- }
and as you see, all we have to do is provide the method name and functionality. No need for the [[prototype]] designation, and this method is included within the class structure.
So, once we add all of our methods, we end up with the same functionality as we had with our Constructor Functions, but the code is cleaner and easier to read. It's a big plus to have the constructor and All of the methods contained within the one Class Structure.
Final Code
- JavaScript
- class RgbConvert {
- constructor ( r = 215, g = 170, b = 80, colorName = 'Bronze' ) {
- this.r = r;
- this.g = g;
- this.b = b;
- this.colorName = .colorName;
- this.calcHSL();
- }
-
- name() {
- const colorName = this.colorName;
- return `Color Name is: ${colorName}`;
- }
- innerRGB() {
- const { r, g, b } = this;
- return `$r}, ${g}, ${b}`;
- }
- rgb() {
- return `rgb(${this.innerRGB()})`;
- }
- rgba(a = 1.0) {
- return `rgba(${this.innerRGB()}, ${a})`;
- }
- hex() {
- const { r, g, b } = this;
- return '#' + ((1 << 24) + (r << 16) + (g << 8) + (b).toString(16).slice(1));
- }
- hsl() {
- const { h, s, l } = this;
- return `hsl(${h}, ${s}%, ${l}%)`;
- }
- opposite() {
- const { h, s, l } = this;
- const newHue = (h + 180) % 360;
- return `hsl(${newHue}, 100%, ${l}%)`;
- }
- calcHSL() {
- let { r, g, b } = this;
- r /= 255;
- g /= 255;
- b /= 255;
- let cmin = Math.min(r, g, b),
- cmax = Math.max(r, g, b),
- delta = cmax - cmin,
- h = 0,
- s = 0,
- l = 0;
- if (delta == 0) h = 0;
- else if (cmax == r)
- h = ((g - b) / delta) % 6;
- else if (cmax == g)
- else
- h = Math.round(h * 60);
- if (h < 0) h += 360;
- l = (cmax + cmin) / 2;
- s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
- s = +(s * 100).toFixed(1);
- l = +(l * 100).toFixed(1);
- this.h = h;
- this.s = s;
- this.l = l;
- }
- }
-
- const bronze = new RgbConvert();
- const teal = new RgbConvert(0, 195, 195, 'Teal');
-
-
- function hexToRgb ( hex = 'a82aaa' ) {
- let r = parseInt(hex.substring(0,2), 16);
- let g = parseInt(hex.substring(2,4), 16);
- let b = parseInt(hex.substring(4), 16);
- return `HEX Color #${hex} = rgb(${r}, ${g}, ${b})`;
- }
-
- const hexColor = hexToRgb();
Notice that when we call bronze, we did not designate any color values. This is to demonstrate how the class will set the default values set within the class structure.
Also notice that we inserted a "base" method to use in the rgb methods. This was done simply to demonstrate how to utilize a method from within another method.
We also added a method to convert RGB to HSL, and also a method to return the opposite HSL value, for determining the opposite color on the color wheel.
And finally, we added a function to convert a hex number into an rgb number.