0

I'm coding an unneeded image manipulation library in hopes of learning how to create a simple library or a framework with javascript in a proper and performant way.

Basic flow chart is like the following:

Create Image Object -> Specify URL and Color Mode -> Initiate object so that main manipulation methods are set to specific ones for the color mode of the current image -> Use those manipulation methods.

I am curious about how more experience coders will approach this situation. So if you think there is a better or more fun way to do it please also share your flow charts, I am eager to learn.

Here is my code snippet:

function ImageLib(URL,colormode){
    this.url = URL;
    this.ColorMode = colormode;
}

ImageLib.prototype.init = function() {
    Switch (this.ColorMode ) {
        case "BlackAndWhite" :
            this.colorEnhance = ImageLib.ColorModeHandlers.BlackAndWhite.method1;
            this.resize = ImageLib.ColorModeHandlers.BlackAndWhite.method2;
            this.sharpen = ImageLib.ColorModeHandlers.BlackAndWhite.method3;
            this.blur = ImageLib.ColorModeHandlers.BlackAndWhite.method4;
            break;

        case "SemiTransparent" :
            this.colorEnhance = ImageLib.ColorModeHandlers.SemiTransparent.method1;
            this.resize = ImageLib.ColorModeHandlers.SemiTransparent.method2;
            this.sharpen = ImageLib.ColorModeHandlers.SemiTransparent.method3;
            this.blur = ImageLib.ColorModeHandlers.SemiTransparent.method4;
            break;

        case "Sephia" :
            this.colorEnhance = ImageLib.ColorModeHandlers.Sephia.method1;
            this.resize = ImageLib.ColorModeHandlers.Sephia.method2;
            this.sharpen = ImageLib.ColorModeHandlers.Sephia.method3;
            this.blur = ImageLib.ColorModeHandlers.Sephia.method4;
            break;

        case "FullColor" :
        case default:
            this.colorEnhance = ImageLib.ColorModeHandlers.FullColor.method1;
            this.resize = ImageLib.ColorModeHandlers.FullColor.method2;
            this.sharpen = ImageLib.ColorModeHandlers.FullColor.method3;
            this.blur = ImageLib.ColorModeHandlers.FullColor.method4;
            break;

        }
    };

}

ImageLib.prototype.ColorModeHandlers.BlackAndWhite = {
    method1: function (){...},
    method2: function (){...},
    method3: function (){...},
    method4: function (){...}
}

ImageLib.prototype.ColorModeHandlers.SemiTransparent = {
    method1: function (){...},
    method2: function (){...},
    method3: function (){...},
    method4: function (){...}
}

ImageLib.prototype.ColorModeHandlers.Sephia = {
    method1: function (){...},
    method2: function (){...},
    method3: function (){...},
    method4: function (){...}
}

ImageLib.prototype.ColorModeHandlers.FullColor = {
    method1: function (){...},
    method2: function (){...},
    method3: function (){...},
    method4: function (){...}
}

image1 = new ImageLib("url","Sephia");
image1.init();
image1.sharpen();
image1.ColorEnhance();
etc...

First of all, obviously ImageLib.prototype.ColorModeHandlers.Sephia ={...} does't work. I couldn't find any article or question on object.prototype.property.property nesting.

How can I do this? What is the correct way to declare nested properties with sub properties and methods.

And since I couldn't find any article on nested properties like this, is this a bad practice?

monroo
  • 429
  • 3
  • 10

3 Answers3

0

ImageLib.prototype.ColorModeHandlers.Sephia ={...} does't work.

As there is no ColorModeHandlers declared yet it will throw error. For this I would use object binding notation as mentioned below, this way you will be able to have nested property. Also you can use other notation to reference property to make your code more crisp (as example look init method).

function ImageLib(URL,colormode){
    this.url = URL;
    this.ColorMode = colormode;
}

ImageLib.prototype.init = function() {
  this.colorEnhance = this.ColorModeHandlers[this.ColorMode].method1;
  this.resize = this.ColorModeHandlers[this.ColorMode].method2;
  this.sharpen = this.ColorModeHandlers[this.ColorMode].method3;
  this.blur = this.ColorModeHandlers[this.ColorMode].method4;    
}

ImageLib.prototype.ColorModeHandlers = {
  SemiTransparent : {
      method1: function (){console.log("semiTransparent method 1")},
      method2: function (){console.log("semiTransparent method 2")},
      method3: function (){console.log("semiTransparent method 3")},
      method4: function (){console.log("semiTransparent method 4")}
  }, BlackAndWhite : {
      method1: function (){console.log("BlackAndWhite method 1")},
      method2: function (){console.log("BlackAndWhite method 2")},
      method3: function (){console.log("BlackAndWhite method 3")},
      method4: function (){console.log("BlackAndWhite method 4")}
  }
}

image1 = new ImageLib("url","Sephia");
image1.init();
image1.sharpen();
image1.ColorEnhance();
Kishor Sharma
  • 599
  • 8
  • 15
0

The reason for it doesn't work is simple. That property doesn't exist at the moment when you try to define nested properties. And thus can't get access to nested. So declaration should have looked like below:

ImageLib.prototype.ColorModeHandlers = {
    BlackAndWhite: {
        method1: function () {...},
        method2: function () {...},
        method3: function () {...},
        method4: function () {...}
    },
    SemiTransparent: {
        method1: function () {...},
        method2: function () {...},
        method3: function () {...},
        method4: function () {...}
    }
 ...
}

However you should now two points about this approach.

  1. When you wrap it in nested levels these methods won't be usable directly. I.e.

    var image1 = new ImageLib("url"); image1.ColorModeHandlers.FullColor.method2(); because this (which most likely will be used in all these methods) inside method2 will refer to the FullColor object. So you need to assign it the way it is in init function of sample code and only after that use via image.resize().

  2. You won't be able to use private states in all methodN functions (i.e. closures) because there will be only one instance of them (because being stored in prototype) and this state will be shared among all instances of ImageLib. The pattern to avoid such stuff is called parasitic inheritance. It's a rather complicated concept for a beginner in javascript. However if you need private states you can get acquainted with it here Parasitic Inheritance in Javascript using pure Prototypal approach

Regarding whether this is a good practice, the answer is very opinionated. Personally I would prefer to keep a bunch of methods not split into sub-objects and pass mode as a parameter. Or these methods would expect to be launched only for an object (ImageLib instance) that has this mode as property and thus would be accessible via this.mode

Community
  • 1
  • 1
Kirill Slatin
  • 6,085
  • 3
  • 18
  • 38
0

First, to answer the easy question:

ImageLib.prototype.ColorModeHandlers.Sephia = {...};

That doesn't work because you don't initialize ImageLib.prototype.ColorModeHandlers anywhere. These assignments should work if you do this first:

ImageLib.prototype.ColorModeHandlers = {};

But why should these handler objects be on the prototype? There doesn't seem to be any reason to put them there. And in fact your code in init() doesn't use ImageLib.prototype.ColorModeHandlers at all; it uses ImageLib.ColorModeHandlers which makes more sense to me. So I would do this instead:

ImageLib.ColorModeHandlers = {};
ImageLib.ColorModeHandlers.BlackAndWhite = {...};

Or even better:

ImageLib.ColorModeHandlers = {
    BlackAndWhite: {...},
    SemiTransparent: {...},
    ...
};

Next, don't meaningless names like method1, method2, etc. Shouldn't those be the same meaningful names used elsewhere in the code, e.g. method1 should be named colorEnhance?

And a most important point: never repeat code!

Any time you find yourself copying and pasting a block of code, think to yourself, "Isn't there a way I could combine these blocks?"

If you use matching names as I suggest above (colorEnhance instead of method1, etc.) then your init function can simply be:

ImageLib.prototype.init = function() {
    var handlers =
        ImageLib.ColorModeHandlers[this.ColorMode] ||
        ImageLib.ColorModeHandlers.FullColor;
    for( var name in handlers ) {
        this[name] = handlers[name];
    }
};

Now we've eliminated all the repetition. And if you later want to add another kind of handler, you don't have to update all the cases in your switch statement, simply add the new handler to each of your ColorModeHandlers objects and it will just work.

Michael Geary
  • 28,450
  • 9
  • 65
  • 75