6

I am using Lightbox2

https://github.com/lokesh/lightbox2/blob/master/js/lightbox.js

And I don't understand why all the inner members of Lightbox are prototyped (Lightbox.prototype.init) and not simply members (Lightbox.init)?

If they are specific to each instance of lightbox wouldn't it be easier to use this.init?

Robert Koritnik
  • 103,639
  • 52
  • 277
  • 404
ilyo
  • 35,851
  • 46
  • 106
  • 159
  • 1
    Each instance that is created inherits the methods from the prototype. This is [not the same as using `this`](http://stackoverflow.com/questions/310870/use-of-prototype-vs-this-in-javascript) and certainly different than assigning the methods to the constructor function. Only instance *specific* data is assigned to `this` inside the constructor. Data that should be shared is assigned to the prototype. – Felix Kling Jun 09 '12 at 10:28
  • possible duplicate of [Use of 'prototype' vs. 'this' in Javascript?](http://stackoverflow.com/questions/310870/use-of-prototype-vs-this-in-javascript). – Felix Kling Jun 09 '12 at 10:30
  • @Felix Kling I am askin specifically about the structure of lightbox, I know the general difference between `this` and `prototype`. I don't understand what the use of `prototype` gives in this particular case. – ilyo Jun 09 '12 at 10:34
  • 2
    The same advantages as in every other situation. For example, `.init()` should be shared between all instances, so it is assigned to the `prototype`. Assigning it to `Lightbox` would not make sense, since instances would not inherit the methods. Assigning it to `this` inside the constructor function is possible, but then each instance has its own copy of the method, which increases memory usage. Of course every general reason to prefer `prototype` over `this` also applies to specific cases. There does not have to be an additional reason to choose it in a certain situation. – Felix Kling Jun 09 '12 at 10:48

2 Answers2

8

Confused? Don't be...

Think of it this way:

  1. Lightbox is your class definition, but it's not yet an instance.

  2. Whatever you put directly on the class is like a static member:

    Lightbox.staticFunc = function() {
        // "this" will not point to instance object
    };
    
  3. Whatever you put on its prototype is a shared instance member:

    Lightbox.prototype.instanceFunc = function() {
        // "this" will point to object instance so members can be accessed
    };
    
  4. When you create an instance of a class, all instance members are accessible throught this keyword, but static ones through class definition:

    var someData = Lightbox.staticFunc();
    var l = new Lightbox();
    l.instanceFunc();
    

Does this clear you understanding of prototype members?

Lightbox code then

The code that you've been looking at means this:

// this is a constructor that accesses instance properties (using "this")
// ------
// since properties are accessed via "this.something" means that they are
//    not shared between instances but are part of one particular instance
// ------
function Lightbox(options) {
    this.options = options;
    this.album = [];
    this.currentImageIndex = void 0;
    this.init();
}

// adding an instance method that will be accessible to lightbox object instance
//    that's why it can also access instance members (using "this")
// ------
// all functions that are defined on the prototype are shared between
//    all instances so they consume less resources because not every
//    object instance created them separately.
// ------
Lightbox.prototype.init = function() {
    this.enable();
    return this.build();
};

But some parts of this code are a bit confusing i.e.

LightboxOptions = (function() {

    function LightboxOptions() {
        this.fileLoadingImage = 'images/loading.gif';
        this.fileCloseImage = 'images/close.png';
        this.resizeDuration = 700;
        this.fadeDuration = 500;
        this.labelImage = "Image";
        this.labelOf = "of";
    }

    return LightboxOptions;

})();

LightboxOptions class is contained within a function closure even though it doesn't define any private data, so the outer immediately executing function could be omitted in this example while having identical results:

LightboxOptions = function() {
    this.fileLoadingImage = 'images/loading.gif';
    this.fileCloseImage = 'images/close.png';
    this.resizeDuration = 700;
    this.fadeDuration = 500;
    this.labelImage = "Image";
    this.labelOf = "of";
};

It would of course be possible to define those functions in a constructor using this but then they wouldn't be shared between instances hence every object instance would define the same function hence consuming more resources. So this is not the same although from the execution point it does look the same:

CustomClass = function() {
    this.prop = true;
};
CustomClass.prototype.method = function() { alert("I'm shared."); };

is slightly different than:

CustomClass = function() {
    this.prop = true;
    this.method = function() { alert("I'm duplicated in every instance."); };
};

The later consumes more resources while function is defined for every object instance.

...and a bit more to completely clear this thing

Suppose we have this class definition:

var C = function() {
    this.prop = true;
    this.method = function() { console.log("Per instance method"); };
}
C.prototype.method = function() { console.log("Shared instance method"); };

What happens here if we call these lines of code

var a = new C();
var b = new C();
a.method();
b.method();
delete a.method;
a.method();
b.method();

What do you think the output would be? You should get at least a little confused what happens after delete? Which method will get deleted? Per instance? Shared? Both? Well as it should be per instance method gets deleted on object instance a, that's why afterwards it reports that the shared method has been called. But only on a. b still has its own per instance method.

So without any further ado, output looks like this:

Per instance method      // a.method
Per instance method      // b.method
Shared instance method   // a.method
Per instance method      // b.method

What about prototype properties

These are different. When you create an object instance all those properties get copied to every object and are not shared. So whatever you do on them within the realm of a particular object will not get reflected to others.

If you'd then delete such property on a particular object it would still be available with its initial value as it was when object got instantiated.

var C = new function() {};
C.prototype.prop = 1;

var a = new C();
var b = new C();

a.prop = 10;   // does not change the value of "b.prop"
delete a.prop; // "a.prop" is now back to 1
Robert Koritnik
  • 103,639
  • 52
  • 277
  • 404
  • 1
    @IlyaD: I added a bit more content related to properties and how they behave. It would clear even more things up. – Robert Koritnik Jun 09 '12 at 11:28
  • there is one last thing I don't understand: why does `function Lightbox(options) {` uses `options` and not `LightboxOptions` and how does `LightboxOptions` becomes `options`? – ilyo Jun 09 '12 at 12:09
  • 1
    @IlyaD: You can name parameters however you want to. They are just names which are local to a function. An instance of `LightboxOptions` is passed as argument to the constructor [in this line](https://github.com/lokesh/lightbox2/blob/master/js/lightbox.js#L348). – Felix Kling Jun 09 '12 at 12:14
  • @IlyaD: And to continue on the Felix' answer... `LightboxOptions` become `options` in the [line number 347](https://github.com/lokesh/lightbox2/blob/master/js/lightbox.js#L347) although for the understandability reasons braces should be used at the end as: `options = new LightboxOptions()` which is a rather strange and unusual thing to not see them being used when instantiating an object using a constructor. – Robert Koritnik Jun 11 '12 at 08:39
4

If they are specific to each instance of lightbox wouldn't it be easier to use this.init?

They shouldn't be that's why they are putting everything in prototype object. When you use prototype, all methods still become available to you only that they do not become instance members.

JavaScript works with prototype chain, when it sees a method, it searches through the prototype chain till it finds the specified method. This process goes till the final Object object if not found in the middle.

You should only create instance members (via this) that you think are reasonable or needed because it adds an overhead (computational waste) if you put unnecessary methods using this keyword eg instance members.

Sarfraz
  • 377,238
  • 77
  • 533
  • 578