706

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. What would be the syntax and why would it be done in that way?

I would like to avoid using third-party libraries - at least at first.
Looking for other answers, I found the article Object-Oriented Programming with JavaScript, Part I: Inheritance - Doc JavaScript that discusses object-oriented programming in JavaScript. Is there a better way to do inheritance?

Daniel X Moore
  • 14,637
  • 17
  • 80
  • 92
Karim
  • 18,347
  • 13
  • 61
  • 70
  • 1
    note: this is a duplicate of http://stackoverflow.com/questions/355848 – Jason S Dec 23 '08 at 15:32
  • 3
    Personally, I like declaring class members inside the function body. I use the 'fixing the this' technique to create closure to make it behave more like a class. I've got a detailed example on my blog: http://ncombo.wordpress.com/2012/12/30/writing-selfish-javascript-dealing-with-the-this-keyword/ – Jon Dec 30 '12 at 10:21
  • 1
    I ported most of C++ OOP functionality to JavaScript with a simple and natural syntax. See my answer here: http://stackoverflow.com/a/18239463/1115652 –  Aug 14 '13 at 18:54
  • There are no classes in JavaScript. But if you want to simulate class-alike behavior in JS, you can. See details in: http://symfony-world.blogspot.com/2013/10/javascript-class-implementation.html – ducin Oct 23 '13 at 21:53
  • 1
    Possible duplicate of [How can I emulate "classes" in JavaScript? (with or without a third-party library)](http://stackoverflow.com/questions/355848/how-can-i-emulate-classes-in-javascript-with-or-without-a-third-party-librar) –  May 30 '16 at 21:58
  • Note that as of 2015 this question is addressed directly with native Class capabilities. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes – patrickgamer Aug 06 '17 at 11:56

19 Answers19

755

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 Stack Overflow. 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 Stack Overflow 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.

Kenan Banks
  • 207,056
  • 34
  • 155
  • 173
  • 48
    But it doesn't work like X language where I learned the one true way that a thingy that is used to make object instances should work :( – Erik Reppen Oct 16 '13 at 02:23
  • 2
    According to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript the properties should also be added to the prototype (" Person.prototype.name= ''; ") – DaveD Mar 31 '14 at 18:54
  • 1
    @DaveD - maybe it did, but doesn't seem to anymore..? – Kieren Johnstone Jun 17 '14 at 07:34
  • @kieren-johnstone Looks like they've removed that – DaveD Jun 18 '14 at 17:26
  • 1
    Prototype is often overlooked for methods... Check out a big difference in performance. Prototyping object properties isn't as big of a deal, and harder to know when you should/shouldn't do it. http://jsperf.com/asdffafaal/2 – BRogers Jun 23 '14 at 19:01
  • 6
    jQuery doesn't even provide any way to create class-like functionality??? (All the classes it has are CSS classes) You should remove it from that part of the answer. – Bergi Jul 12 '14 at 09:53
  • 7
    From secound half of 2015 EcmaScript 6 new standard was released, so I propose to do it the new way (much cleaner and easer) http://es6-features.org/#ClassDefinition – DevWL Mar 24 '16 at 06:08
  • Your "lean towards jQuery" link is broken. – adamdport Apr 25 '16 at 15:49
214

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
  • 363,080
  • 75
  • 446
  • 653
  • "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". If you call a function without the new keyword, this will refer to the calling context, by default the global object (window). In strict mode, undefined is the default. call, apply and bind takes calling context as first parameter. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#Function_context – Elias Hasle May 03 '19 at 08:18
86

ES2015 Classes

In the ES2015 specification, you can use the class syntax which is just sugar over the prototype system.

class Person {
  constructor(name) {
    this.name = name;
  }
  toString() {
    return `My name is ${ this.name }.`;
  }
}

class Employee extends Person {
  constructor(name, hours) {
    super(name);
    this.hours = hours;
  }
  toString() {
    return `${ super.toString() } I work ${ this.hours } hours.`;
  }
}

Benefits

The main benefit is that static analysis tools find it easier to target this syntax. It is also easier for others coming from class-based languages to use the language as a polyglot.

Caveats

Be wary of its current limitations. To achieve private properties, one must resort to using Symbols or WeakMaps. In future releases, classes will most likely be expanded to include these missing features.

Support

Browser support isn't very good at the moment (supported by nearly everyone except IE), but you can use these features now with a transpiler like Babel.

Resources

Dale
  • 5,607
  • 2
  • 22
  • 21
56

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
  • 14,637
  • 17
  • 80
  • 92
  • @DanielXMoore "Instance variables are shared among individual instances of a class" Those aren't instance variables, those are static/class variables. – JAB Jul 02 '13 at 19:05
  • 2
    @JAB That is incorrect, static/class variables are shared among *all* instances of a class. Each instance has its own instance variables. – Daniel X Moore Jul 02 '13 at 19:34
  • (Put another way, using the normal meaning of the term "instance variable", whether or not a variable is one is orthogonal to the level of accessibility of the variable.) – JAB Jul 02 '13 at 19:55
  • 2
    You almost sounded like a superhero for claiming the best xD – Aesthetic Sep 14 '16 at 10:23
41
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
  • 5,295
  • 3
  • 34
  • 30
  • This is a very elegant way to build a usable object structure without having to import anything. I was using Resig's class system, but I may like this better. Thank you. – Tim Scollick Sep 03 '13 at 15:55
  • 29
    The issue with this approach is that every time you create a new Animal instance it's going to redefine the functions rather than only defining them once with prototype. – Justin Oct 14 '13 at 17:29
37

Following are the ways to create objects in javascript, which I've used so far

Example 1:

obj = new Object();
obj.name = 'test';
obj.sayHello = function() {
    console.log('Hello '+ this.name);
}

Example 2:

obj = {};
obj.name = 'test';
obj.sayHello = function() {
    console.log('Hello '+ this.name);
}
obj.sayHello();

Example 3:

var obj = function(nameParam) {
    this.name = nameParam;
}
obj.prototype.sayHello = function() {
    console.log('Hello '+ this.name);
}

Example 4: Actual benefits of Object.create(). please refer [this link]

var Obj = {
    init: function(nameParam) {
        this.name = nameParam;
    },
    sayHello: function() {
        console.log('Hello '+ this.name);
    }
};
var usrObj = Object.create(Obj);  // <== one level of inheritance

usrObj.init('Bob');
usrObj.sayHello();

Example 5 (customised Crockford's Object.create):

Object.build = function(o) {
   var initArgs = Array.prototype.slice.call(arguments,1)
   function F() {
      if((typeof o.init === 'function') && initArgs.length) {
         o.init.apply(this,initArgs)
      }
   }
   F.prototype = o
   return new F()
}
MY_GLOBAL = {i: 1, nextId: function(){return this.i++}}  // For example

var userB = {
    init: function(nameParam) {
        this.id = MY_GLOBAL.nextId();
        this.name = nameParam;
    },
    sayHello: function() {
        console.log('Hello '+ this.name);
    }
};
var bob = Object.build(userB, 'Bob');  // Different from your code
bob.sayHello();


To keep answer updated with ES6/ ES2015

A class is defined like this:

class Person {
    constructor(strName, numAge) {
        this.name = strName;
        this.age = numAge;
    }

    toString() {
        return '((Class::Person) named ' + this.name + ' & of age ' + this.age + ')';
    }
}

let objPerson = new Person("Bob",33);
console.log(objPerson.toString());
Amol M Kulkarni
  • 21,143
  • 34
  • 120
  • 164
  • 1
    @Justin: Please let me know what is not valid? – Amol M Kulkarni Aug 12 '15 at 07:24
  • While studying these notations, I also stumbled upon this.set(). For ex: this.set('port', 3000). My guess is, this is used to set the port property for the object. If so, why do we not directly use: {port: 3000}. Is there any documentation where I can get more details. – adityah Jun 23 '17 at 21:22
23

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
  • 5,885
  • 6
  • 41
  • 55
  • 25
    Is it just me? How the heck is this more elegant? I would call function definitions with actual `'strings'` as names many things, but elegant is not one of them... – fgysin Jan 16 '13 at 12:28
  • 4
    @JAB, but reflection is the exception, not the rule. With the above method, you have to *declare all your methods* with strings. – Kirk Woll Dec 22 '13 at 17:05
16

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
  • 74,572
  • 17
  • 113
  • 180
  • I like it. I most often use JS's standard syntax cause I don't like the idea of copying functions into each object instance. I always missed the beauty of self contained solution though, and this solves it pretty well. – Lukasz Korzybski Feb 03 '11 at 14:50
  • 1
    Not sure but I understood that defining prototype function inside the scope (somewhat as a closure) of a function results in a memory leak since the garbage collector can't come there in the instance of those classes. – Sanne Jul 26 '14 at 21:51
14

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.

Hearaman
  • 8,466
  • 13
  • 41
  • 58
Sam
  • 6,167
  • 6
  • 26
  • 24
  • The obj.instancevar1 = var1 line isn't necessary, since the inner object will have access to getSomeObj()'s parameters. – Kenan Banks Dec 22 '08 at 23:28
  • 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 Dec 22 '08 at 23:28
  • Just saw Triptych's comment. I see. So, you could just do something like "instancevar1: var1" where the inner object is being instantiated. – Karim Dec 22 '08 at 23:29
  • Exactly... when you use {} to define an object, it has access to variables that are currently in scope. – Sam Dec 22 '08 at 23:32
  • 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 Dec 22 '08 at 23:37
  • 10
    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 Dec 23 '08 at 04:10
  • This is very helpful. It appears this is more of the natural flow of JavaScript if you take it as it is, which then sets you up for object creation and linkage, now that the factory is shipping them out! – klewis Jun 01 '18 at 21:18
12
var Student = (function () {
    function Student(firstname, lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
        this.fullname = firstname + " " + lastname;
    }

    Student.prototype.sayMyName = function () {
        return this.fullname;
    };

    return Student;
}());

var user = new Student("Jane", "User");
var user_fullname = user.sayMyName();

Thats the way TypeScript compiles class with constructor to JavaScript.

Mick
  • 8,203
  • 10
  • 44
  • 66
9

You probably want to create a type by using the Folding Pattern:

    // Here is the constructor section.
    var myType = function () {
        var N = {}, // Enclosed (private) members are here.
            X = this; // Exposed (public) members are here.

        (function ENCLOSED_FIELDS() {
            N.toggle = false;
            N.text = '';
        }());

        (function EXPOSED_FIELDS() {
            X.count = 0;
            X.numbers = [1, 2, 3];
        }());

        // The properties below have access to the enclosed fields.
        // Careful with functions exposed within the closure of the
        // constructor, each new instance will have it's own copy.
        (function EXPOSED_PROPERTIES_WITHIN_CONSTRUCTOR() {
            Object.defineProperty(X, 'toggle', {
                get: function () {
                    var before = N.toggle;
                    N.toggle = !N.toggle;
                    return before;
                }
            });

            Object.defineProperty(X, 'text', {
                get: function () {
                    return N.text;
                },
                set: function (value) {
                    N.text = value;
                }
            });
        }());
    };

    // Here is the prototype section.
    (function PROTOTYPE() {
        var P = myType.prototype;

        (function EXPOSED_PROPERTIES_WITHIN_PROTOTYPE() {
            Object.defineProperty(P, 'numberLength', {
                get: function () {
                    return this.numbers.length;
                }
            });
        }());

        (function EXPOSED_METHODS() {
            P.incrementNumbersByCount = function () {
                var i;
                for (i = 0; i < this.numbers.length; i++) {
                    this.numbers[i] += this.count;
                }
            };
            P.tweak = function () {
                if (this.toggle) {
                    this.count++;
                }
                this.text = 'tweaked';
            };
        }());
    }());

That code will give you a type called myType. It will have internal private fields called toggle and text. It will also have these exposed members: the fields count and numbers; the properties toggle, text and numberLength; the methods incrementNumbersByCount and tweak.

The Folding Pattern is fully detailed here: Javascript Folding Pattern

intrepidis
  • 2,870
  • 1
  • 34
  • 36
9

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
  • 73,323
  • 21
  • 116
  • 148
  • 1
    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. – Kenan Banks Dec 22 '08 at 23:36
  • 1
    Is it necessary (sorta) in that case, but not the simple case you've provided. – Kenan Banks Dec 23 '08 at 16:35
3

Code golf for @liammclennan's answer.

var Animal = function (args) {
  return {
    name: args.name,

    getName: function () {
      return this.name; // member access
    },

    callGetName: function () {
      return this.getName(); // method call
    }
  };
};

var cat = Animal({ name: 'tiger' });
console.log(cat.callGetName());
Community
  • 1
  • 1
tponthieux
  • 1,502
  • 5
  • 18
  • 30
2

Object Based Classes with Inheritence

var baseObject = 
{
     // Replication / Constructor function
     new : function(){
         return Object.create(this);   
     },

    aProperty : null,
    aMethod : function(param){
      alert("Heres your " + param + "!");
    },
}


newObject = baseObject.new();
newObject.aProperty = "Hello";

anotherObject = Object.create(baseObject); 
anotherObject.aProperty = "There";

console.log(newObject.aProperty) // "Hello"
console.log(anotherObject.aProperty) // "There"
console.log(baseObject.aProperty) // null

Simple, sweet, and gets 'er done.

Ulad Kasach
  • 11,558
  • 11
  • 61
  • 87
2

Based on the example of Triptych, this might even be simpler:

    // Define a class and instantiate it
    var ThePerson = new function Person(name, gender) {
        // Add class data members
        this.name = name;
        this.gender = gender;
        // Add class methods
        this.hello = function () { alert('Hello, this is ' + this.name); }
    }("Bob", "M"); // this instantiates the 'new' object

    // Use the object
    ThePerson.hello(); // alerts "Hello, this is Bob"

This only creates a single object instance, but is still useful if you want to encapsulate a bunch of names for variable and methods in a class. Normally there would not be the "Bob, M" arguments to the constructor, for example if the methods would be calls to a system with its own data, such as a database or network.

I am still too new with JS to see why this does not use the prototype thing.

Roland
  • 4,619
  • 7
  • 49
  • 81
2

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.

Ryan Florence
  • 13,361
  • 9
  • 46
  • 63
1

A base

function Base(kind) {
    this.kind = kind;
}

A class

// Shared var
var _greeting;

(function _init() {
    Class.prototype = new Base();
    Class.prototype.constructor = Class;
    Class.prototype.log = function() { _log.apply(this, arguments); }
    _greeting = "Good afternoon!";
})();

function Class(name, kind) {
    Base.call(this, kind);
    this.name = name;
}

// Shared function
function _log() {
    console.log(_greeting + " Me name is " + this.name + " and I'm a " + this.kind);
}

Action

var c = new Class("Joe", "Object");
c.log(); // "Good afternoon! Me name is Joe and I'm a Object"
Mikael Dúi Bolinder
  • 2,080
  • 2
  • 19
  • 44
0

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 JavaScript world. Sorry, it seems that that content has been removed. Here's a link to archive.org copy instead. Works today. :P

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vilx-
  • 104,512
  • 87
  • 279
  • 422
  • 2
    Object-oriented? I thought it was [functional](https://en.wikipedia.org/wiki/Functional_programming). – Peter Mortensen Aug 03 '13 at 06:28
  • I think it would be more explanatory to explain that functions in javascript are objects, and the bracket-scope rules of javascript make each function block encapsulating. – mibbit Sep 17 '18 at 01:31
0

//new way using this and new
function Persons(name) {
  this.name = name;
  this.greeting = function() {
    alert('Hi! I\'m ' + this.name + '.');
  };
}

var gee=new Persons("gee");
gee.greeting();

var gray=new Persons("gray");
gray.greeting();

//old way
function createPerson(name){
 var obj={};
 obj.name=name;
 obj.greeting = function(){
 console.log("hello I am"+obj.name);
 }; 
  return obj;
}

var gita=createPerson('Gita');
gita.greeting();
Avinash Maurya
  • 332
  • 3
  • 4
  • great that look really a bit like class syntax. nearly shat @user1115652 suggest: "I ported most of C++ OOP functionality to JavaScript with a simple and natural syntax. See my answer here: stackoverflow.com/a/18239463/1115652" – SL5net Jun 28 '20 at 06:42