views:

2985

answers:

14

While I'm not entirely new to javascript I have very little knowledge when it comes to best practices. In particular, I prefer to use OOP in large scale projects like the one I'm working on right now. I need to create several classes in javascript but, if I'm not mistaken, there are at least a couple of ways to go about doing that. Can anyone in the know share their syntax and maybe explain briefly why they choose to do it that way?

Or am I way off base here and there's only one way to define classes?

EDIT: I would like to avoid using third-party libraries - at least at first.
EDIT2: Looking for other answers I found this article that discusses object-oriented programming in javascript. Would be interested to hear people's take on it - in particular the parts about inheritance. Is there a better way to do inheritance?

+21  A: 

Here's the way to do it without using any external libraries:

// Define a class like this
function Person(name, gender){

   // Add object properties like this
   this.name = name;
   this.gender = gender;
}

// Add methods like this.  All Person objects will be able to invoke this
Person.prototype.speak = function(){
    alert("Howdy, my name is" + this.name);
}

// Instantiate new objects with 'new'
var person = new Person("Bob", "M");

// Invoke methods like this
person.speak(); // alerts "Howdy, my name is Bob"

Now the real answer is a whole lot more complex than that. For instance, there is no such thing as classes in Javascript. Javascript uses a prototype-based inheritance scheme.

In addition, there are numerous popular Javascript libraries that have their own style of approximating class-like functionality in Javascript. You'll want to check out at least Prototype and jQuery.

Deciding which of these is the "best" is a great way to start a holy war on SO. If you're embarking on a larger Javascript-heavy project, it's definitely worth learning a popular library and doing it their way. I'm a Prototype guy, but SO seems to lean towards jQuery.

As far as there being only "one way to do it", without any dependencies on external libraries, the way I wrote is pretty much it.

Triptych
Yeah, I want to avoid using a library at this point so I can better understand javascript.
Karim
I was afraid you were going to use that syntax. I find it appalling coming from "true" OO languages. Sigh.
Karim
Haha, well that's the syntax naked Javascript's got. Still if you're learning Javascript from scratch, it's useful to know.
Triptych
if i also needed a Student to inherit from Person, how would this be specified?
Steven A. Lowe
@Stevenfunction Student(){};Student.prototype = new Person();
seanalltogether
@Karim Javascript is true OO - don't confuse classes with objects
Greg
+2  A: 

If you're going for simple, you can avoid the "new" keyword entirely and just use factory methods. I prefer this, sometimes, because I like using JSON to create objects.

function getSomeObj(var1, var2){
  var obj = {
     instancevar1: var1,
     instancevar2: var2,
     someMethod: function(param){  //stuff; }
  };
  return obj;
}

var myobj = getSomeObj("var1", "var2");
myobj.someMethod("bla");

I'm not sure what the performance hit is for large objects, though.

Sam
The obj.instancevar1 = var1 line isn't necessary, since the inner object will have access to getSomeObj()'s parameters.
Triptych
Wow. That makes my brain hurt but there is a certain elegance to it. So the "obj.instancevar1 = var1" part is the beginning of a sort of constructor, i suppose?
Karim
Just saw Triptych's comment. I see. So, you could just do something like "instancevar1: var1" where the inner object is being instantiated.
Karim
Exactly... when you use {} to define an object, it has access to variables that are currently in scope.
Sam
Also, there is no "private" keyword, so even outside of getSomeObj() you can get/set instancevar1. Once you get closures and "this" keyword, you are golden. Javascript is quite a beautiful and flexible language.
Sam
I will agree with the flexible. I'm not sold on the beautiful yet.
Karim
With this approach you loose the ability to inherit, and since you aren't using obj.prototype.something you are defining the functions every time you are using the object = more memory and slower.
some
A: 

If you haven't settled on a javascript library yet, and if you are looking for sanity in your life, make sure to check out ExtJS in your exploration.

http://extjs.com

Mike
I'm actually using YUI right now for a variety of reasons but it's not clear to me if they provide some wrapper for defining classes in javascript.
Karim
I had a really poor experience with ExtJS... it's an incredibly big (not necessarily bloated) library that would hang my browser for 2 seconds on every page load.
nickf
Little late... @nickf - ExtJS is just as fast or slow as other libraries, You may be running into firebug slowness or bringing in everything in the library that you may not need. @Karim - they do, and there is an extend() and override() API call to provide inheritance.
Mike
A: 

Javascript is Object-Oriented, but it's radically different than other OOP languages like Java, C# or C++. Don't try to understand it like that. Throw that old knowledge out and start anew. Javascript needs a different thinking.

I'd suggest to get a good manual or something on the subject. I myself found ExtJS Tutorials the best for me, although I haven't used the framework before or after reading it. But it does give a good explanation about what is what in JS world.

Vilx-
A: 

The simple way is:

function Foo(a) {
  var that=this;

  function privateMethod() { .. }

  // public methods
  that.add = function(b) {
    return a + b;
  };
  that.avg = function(b) {
    return that.add(b) / 2; // calling another public method
  };
}

var x = new Foo(10);
alert(x.add(2)); // 12
alert(x.avg(20)); // 15

The reason for that is that this can be bound to something else if you give a method as an event handler, so you save the value during instantiation and use it later.

Edit: it's definitely not the best way, just a simple way. I'm waiting for good answers too!

orip
The that=this construct is not necessary here. Also, the add() and avg() methods will be copied for every "instance" of class Foo, rather than shared between them.
Triptych
that=this is necessary if you want to use the method as an event handler directly (that.foo = function() { .. }; ... element.onclick = x.foo;). Methods copied for each instance allows private variables and functions that only they methods can see.
orip
Is it necessary (sorta) in that case, but not the simple case you've provided.
Triptych
+1  A: 

Pro Javascript Techniques by John Resig (jQuery guy) will get you up to speed with this stuff real quick

Also check out Douglas Crockford's javascript site

Scott Evernden
Thanks for the book recommendation.
Karim
A: 

This question was answered multiple times from different perspectives. This is the primer I wrote answering the similar question Prototype based object orientation. The good, the bad and the ugly?. I hope you'll find it useful.

Eugene Lazutkin
+6  A: 

I think you should read Douglas Crockford's Prototypal Inheritance in JavaScript and Classical Inheritance in JavaScript.

Examples from his page:

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};

Effect? It will allow you to add methods in more elegant way:

function Parenizor(value) {
    this.setValue(value);
}

Parenizor.method('setValue', function (value) {
    this.value = value;
    return this;
});

I also recommend his videos: Advanced JavaScript.

You can find more videos on his page: http://javascript.crockford.com/ In John Reisig book you can find many examples from Douglas Crockfor's website.

Jarek
I actually watched that series and found it really interesting. I was very much interested, though, in getting a sampling of implementations from a variety of people.
Karim
+10  A: 

The best way to define a class in JavaScript is to not define a class.

Seriously.

There are several different flavors of object-orientation, some of them are:

  • class-based OO (first introduced by Smalltalk)
  • prototype-based OO (first introduced by Self)
  • multimethod-based OO (first introduced by CommonLoops, I think)
  • predicate-based OO (no idea)

And probably others I don't know about.

JavaScript implements prototype-based OO. In prototype-based OO, new objects are created by copying other objects (instead of being instantiated from a class template) and methods live directly in objects instead of in classes. Inheritance is done via delegation: if an object doesn't have a method or property, it is looked up on its prototype(s) (i.e. the object it was cloned from), then the prototype's prototypes and so on.

In other words: there are no classes.

JavaScript actually has a nice tweak of that model: constructors. Not only can you create objects by copying existing ones, you can also construct them "out of thin air", so to speak. If you call a function with the new keyword, that function becomes a constructor and the this keyword will not point to the current object but instead to a newly created "empty" one. So, you can configure an object any way you like. In that way, JavaScript constructors can take on one of the roles of classes in traditional class-based OO: serving as a template or blueprint for new objects.

Now, JavaScript is a very powerful language, so it is quite easy to implement a class-based OO system within JavaScript if you want to. However, you should only do this if you really have a need for it and not just because that's the way Java does it.

Jörg W Mittag
Thanks for the deeper analysis.
Triptych
A: 

also check out Pro Javascript Techniques by John Resig (jQuery guy)

Scott Evernden
A: 

Because I will not admit the YUI/Crockford factory plan and because I like to keep things self contained and extensible this is my variation:

function Person(params)
{
  this.name = params.name || defaultnamevalue;
  this.role = params.role || defaultrolevalue;

  if(typeof(this.speak)=='undefined') //guarantees one time prototyping
  {
    Person.prototype.speak = function() {/* do whatever */};
  }
}

var Robert = new Person({name:'Bob'});

where ideally the typeof test is on something like the first method prototyped

annakata
A: 

MooTools (My Object Oriented Tools) is centered on the idea of classes. You can even extend and implement with inheritance.

When mastered, it makes for ridiculously reusable, powerful javascript.

rpflo
A: 
var Animal = function(options) {
    var name = options.name;
    var animal = {};

    animal.getName = function() {
        return name;
    };

    var somePrivateMethod = function() {

    };

    return animal;
};

// usage
var cat = Animal({name: 'tiger'});
liammclennan
A: 

I prefer to use Daniel X. Moore's {SUPER: SYSTEM}. This is a discipline that provides benefits such as true instance variables, trait based inheritance, class hierarchies and configuration options. The example below illustrates the use of true instance variables, which I believe is the biggest advantage. If you don't need instance variables and are happy with only public or private variables then there are probably simpler systems.

function Person(I) {
  I = I || {};

  Object.reverseMerge(I, {
    name: "McLovin",
    age: 25,
    homeState: "Hawaii"
  });

  return {
    introduce: function() {
      return "Hi I'm " + I.name + " and I'm " + I.age;
    }
  };
}

var fogel = Person({
  age: "old enough"
});
fogel.introduce(); // "Hi I'm McLovin and I'm old enough"

Wow, that's not really very useful on it's own, but take a look at adding a subclass:

function Ninja(I) {
  I = I || {};

  Object.reverseMerge(I, {
    belt: "black"
  });

  // Ninja is a subclass of person
  return Object.extend(Person(I), {
    greetChallenger: function() {
      return "In all my " + I.age + " years as a ninja, I've never met a challenger as worthy as you...";
    }
  });
}

var resig = Ninja({name: "John Resig"});

resig.introduce(); // "Hi I'm John Resig and I'm 25"

Another advantage is the ability to have modules and trait based inheritance.

// The Bindable module
function Bindable() {

  var eventCallbacks = {};

  return {
    bind: function(event, callback) {
      eventCallbacks[event] = eventCallbacks[event] || [];

      eventCallbacks[event].push(callback);
    },

    trigger: function(event) {
      var callbacks = eventCallbacks[event];

      if(callbacks && callbacks.length) {
        var self = this;
        callbacks.forEach(function(callback) {
          callback(self);
        });
      }
    },
  };
}

An example of having the person class include the bindable module.

function Person(I) {
  I = I || {};

  Object.reverseMerge(I, {
    name: "McLovin",
    age: 25,
    homeState: "Hawaii"
  });

  var self = {
    introduce: function() {
      return "Hi I'm " + I.name + " and I'm " + I.age;
    }
  };

  // Including the Bindable module
  Object.extend(self, Bindable());

  return self;
}

var person = Person();
person.bind("eat", function() {
  alert(person.introduce() + " and I'm eating!");
});

person.trigger("eat"); // Blasts the alert!

Disclosure: I am Daniel X. Moore and this is my {SUPER: SYSTEM}. It is the best way to define a class in JavaScript.

Daniel X Moore