views:

131

answers:

2

I'm relatively new to JS and I'm having issues properly emulating OOP principles.

I guess I have two questions. Question the first is regarding the many ways to declare variables.

Say I have a class:

function clazz(a)
{
    this.b = 2;
    var c = 3;
    this.prototype.d = 4; // or clazz.prototype.d = 4?
}

var myClazz = new clazz(1);

Am I correct in the following assessments:

a is a private variable that is instance specific (i.e. different instances of clazz will have unique and independent variables 'a'). It can be accessed from within clazz as: 'a'.

b is a public variable that is instance specific. It can be accessed from within clazz as 'this.b' and from outside clazz as 'myClazz.b'.

c is a private variable that is static, or class specific (i.e. different instances of clazz will share the same 'c' variable). It can be accessed from within any instance of clazz as 'c' and changes in instance of clazz are reflected in all instances of clazz.

d is a public variable that is static/class specific. It can be accessed from anywhere via 'clazz.prototype.d' or 'myClazz.prototype.d'.

The overall issue I have with my understanding of the variable scheme is that there's no way to declare a private variable that is NOT static (i.e. a unique version for every instance of the class).

The second question is with respect to different types of class declarations.

I've been using:

var MySingleton = new function() {...};

to create singletons. Is this correct? I'm also unsure as to the effect of the "new" keyword in this situation as well as appending () function braces to the end of the declaration as so:

var MySingleton = new function() {...}();

I've been using this pattern to declare a class and then instantiate instances of that class:

function myClass() {...};
var classA = new myClass();
var classB = new myClass();

Is this the proper method?

+5  A: 

You are right for a and b:

a is an argument, available only within the scope of the constructor function.

bis a public instance variable, available on all instances created with that constructor function.

c is a private variable, accessible only within the constructor function.

The d declaration is invalid, because the prototype object is meant to be used only on constructor functions, like Clazz.prototype.d = 3;, if you do it like this, the variable will be shared, but you can assign a value on an specific instance, and the default value will be shadowed (through the prototype chain).

For "private variables" you can use the way you declare c, for example:

function Clazz(){
    var c = 3; // private variable

    this.privilegedMethod = function () {
      alert(c);
    };
}

Privileged methods, are public, but they can access "private" variables declared inside the constructor function.

For creating singletons, the easiest way is maybe to use an object literal, like:

var myInstance = {
  method1: function () {
    // ...
  },
  method2: function () {
    // ...
  }
};

And if you want private members on your singleton instance, you can:

var myInstance = (function() {
  var privateVar = '';

  function privateMethod () {
    // ...
  }

  return { // public interface
    publicMethod1: function () {
      // all private members are accesible here
    },
    publicMethod2: function () {
    }
  };
})();

This is has been called the module pattern, it basically allows you to encapsulate private members on an object, by taking advantage of the use of closures.

More info:

Edit: About the syntax you post:

var mySingleton = new (function() {
  // ...
})();

By using the new operator, you are declaring and using in one step an "anonymous constructor function", which will generate a new object instance, it is valid but I personally would prefer the "module" pattern approach, to create my own object instance (and avoid new).

Also, reading new function () {}, I think that is not really intuitive and may create confusion, if you don't understand well how the new operator works.

About the parentheses, they are optional, the new operator will call the function constructor without parameters if you don't add them (see ECMA-262, 11.2.2).

CMS
+1. A very complete and informative answer.
Lior Cohen
Thank you for the informative reply.A followup: What are the tangible differences between the module approach to singleton declaration and the style referenced in the original post?
Cmc
**@Lior:** thanks!, **Cmc:** I wrote a bit about the syntax you posted...
CMS
+1  A: 

OK, lets go over this:

  1. 'a' is an argument passed to the constructor of your class. It will only exist for the duration of the constructor's invocation. This means you should probably store its value somewhere.

  2. 'b' is a public instance member. It is instance specific (altho, since you're assigning the value in the constructor, all instances will initially have the same value for 'b').

  3. 'c' is a private instance member. It will, however, only be accessible inside your constructor since it is only defined in that scope. Unless you refer to it from a closure inside your constructor function, its fate will be similar to that of 'a' above.

  4. 'd' is a public instance member. Each instance of your class will have a member 'd' with the value 4 initially. Note however that assigning a reference type object to a prototype member of your class (such as 'd') will make every instance member 'd' refer to the same object. Example:

    MyClass.prototype.d = { prop1: 'val1', prop2: 'val2' };        
    var a = new MyClass();
    var b = new MyClass();        
    a.d.prop1 = 'foo'; // is the same as: b.d.prop1 = 'foo';
    
  5. Static members of the class are defined using:

    function MyClass()
    {
      // ...
    }    
    MyClass.staticMemeber = 'I am a static member';
    

    You should probably not treat MyClass.prototype as a place to hold your static members/methods.Everything assigned to a classes' prototype is in turn a member of each of its instances.

  6. When ()'s are attached to a function definition (right after the block), the function is executed. That means:

    var myFunc = function() { alert('blah'); }();
    

    would result in nothing more than a method invocation. The following code:

     var MySingleton = new function() {...}();
    

    would mean 'use the return value from function() as the constructor for MySingleton'.

Lior Cohen