There are lots of ways of doing the same thing in JavaScript. I have however picked up some ways, and some ways I frankly don\'t understand. Could anyone please help me clar
var object = new function () {
//
}
var myObject = new object();
No, that is not correct. The constructor function should be separate from the creation of the object:
function myClass(constructparam1) {
this.public_member = constructparam1; // Can be accessed from everywhere.
}
The methods should be in the prototype for the constructor, not the instance:
myClass.prototype.semi_public_members = function() {
// Will be public
}
Call the constructor with the new
keyword to create the instance:
var obj = new myClass(1337);
Local variables inside the constructor function can only be accessed inside that function. If you wan't local variables in your class, you need a function around it to create a closure, so that the methods in the prototype can access the variables:
var myClass = (function(){
var local_variable;
function constructor(constructparam1) {
this.public_member = constructparam1; // Can be accessed from everywhere.
}
constructor.prototype.semi_public_members = function() {
// Will be public
alert(local_variable); // can access private variables
}
return constructor;
})();
Object literals is just a simple but limited way to create objects in one go. They don't have a prototype, so if you want them to have methods, you have to assign them to properties:
var obj = {
public_member: 1337,
semi_public_members: function(){
alert(this.public_member);
}
};
I think there are some concepts that seem to be missing but I'll try answering as much as I can.
So a class can be made like this...Is this all correct so far?
It's close but is not entirely correct. You don't need new
to create a constructor function, you only need it when creating a new instance of the "class".
function Klass() { ... } // capitalized name as convention for constructors
var myKlass = new Klass(); //<-- Need "new" here
Public methods can be attached to the instance or to the prototype. When you attach it to the prototype the method will be shared across all instances, saving some memory.
Klass.prototype = {
publicSharedMethod: function() {
var self = this;
}
}
Then someone likes the self-executing anonymous function... What is the point of that...
The module pattern (or self-execution function) approach is a bit different because you're typically not dealing with the prototype although you can. It's just a function that returns a literal object with properties and methods but I mainly use it for singletons when you don't need instances of that object:
var Singleton = (function() {
var private = 'foo';
var public = 'baz';
var publicMethod = function() { ... }
// Expose public methods and properties
return {
public: public
publicMethod: publicMethod
}
}());
And lastly you have the object literal notation that I don't understand.
That's just a regular object in JavaScript, similar to what PHP calls "associative array". Literal objects syntax are the base for JSON, but JSON has more limitations in terms of formatting; JavaScript is more forgiving so you can have unquoted properties and methods for example.
Why would you prototype instead of making the class correctly the first time?
The point here is to understand that JavaScript is not a traditional object oriented language, so there are no classes, and you shouldn't be thinking about classes. But prototypes are very powerful, I'd say even more powerful than classes. There are many examples online on how to replicate classes with prototypes but not the other way around.
Explaining the behaviour of different things in a constructed object by example:
// Defined as a variable from an anonymous function
// so that there is scope closure over variables
// shared across all instances and the prototype.
// If this isn't important, you don't need to close
// scope around it, so define directly
var ConstructedObject = (function constructorCreator () {
// Define any variables/methods to be shared across
// all instances but not polluting the namespace
var sharedVariable = 'foo';
// Next the actual constructor
function ConstructedObject () {
// Variables here are normally used to help
// each instance and will be kept in memory as
// long as the instance exists
var instanceVariable = 'bar';
// instance-specific properties get defined
// using the "this" keyword, these are the
// properties expected to be changed across
// each different instance
this.instanceProperty = true;
this.instanceMethod = function () { return instanceVariable; };
this.changeInstanceVar = function () { instanceVariable = 'foo'; };
// you do have access to the shared
// variables here if you need them.
}
// After the constructor, you set up the
// prototype, if any. This is an object of shared
// properties and methods to be inherited by every
// instance made by the constructor, and it also
// inherits the prototype's prototype, too.
// Lets use a literal object for simplicity.
ConstructedObject.prototype = {
// Accessing the instance to which a method
// applies is done using the "this" keyword,
// similar to in the constructor
sharedMethod : function () { return [sharedVariable, this.instanceMethod(),this.instanceProperty]; },
changeSharedVar : function () { sharedVariable = 'bar'; }
// properties may also be defined
};
// Finally, the constructor is returned so it
// can be kept alive outside of the anonymous
// function used to create it
return ConstructedObject;
// and the anonymous function is called to execute
// what we've done so far
})();
After executing the above code, you have a constructor that creates objects with both instance-specific and shared variables. Now let's look at how they behave by creating two of instances and comparing them before and after some changes.
// First create the two instances
var myObjA = new ConstructedObject(),
myObjB = new ConstructedObject();
// Now compare them, the sharedMethod method we
// used in the prototype offers an easy way to
// do this
console.log( myObjA.sharedMethod(), myObjB.sharedMethod() );
// ["foo", "bar", true] ["foo", "bar", true]
// Next lets change the different variables in
// myObjB so we can see what happens, again the
// change* methods defined before let us do this
// easily
myObjB.changeInstanceVar();
myObjB.changeSharedVar();
// For completeness, lets also change the property
// on myObjB.
myObjB.instanceProperty = false;
// Now when we compare them again, we see that our
// changes to the myObjB instance have only changed
// the shared variables of myObjA
console.log( myObjA.sharedMethod(), myObjB.sharedMethod() );
// ["bar", "bar", true] ["bar", "foo", false]
Here are the two logged statements together for easier viewing
// myObjA myObjB
["foo", "bar", true] ["foo", "bar", true]
["bar", "bar", true] ["bar", "foo", false]