17

What is the best way to build constructors in JavaScript using object literal notation?

var myObject = {
 funca : function() {
  //...
 },

 funcb : function() {
  //...
 }
};

I want to be able to call

var myVar = new myObject(...);

And pass the arguments to a constructor function inside myObject.

mrwooster
  • 23,789
  • 12
  • 38
  • 48
  • What exactly do you want to achieve? – Felix Kling Mar 03 '11 at 10:26
  • I am looking for the best way to define class like structures in JavaScript. I am aware that you can do it through prototyping, but looking around on the web, I have found more and more javascript libraries using object literal (not JSON ;) ) notation, rather than prototyping... why is this? – mrwooster Mar 03 '11 at 10:30
  • Can you give any particular example? If one needs only one object of a kind, then there is no need to have a constructor function. – Felix Kling Mar 03 '11 at 10:33
  • @Felix I am looking for a more general case... I am building a javascript library and am looking for the best way to implement a class like structure. Multiple instances of the same object can occur in the same script. So... my end result would be something like .... var o = new myObject(someArgs); ... o.doSomething(someArg); ... o.doSomethingElse(someArg);... I can implement this using prototyping, but have seen the above notation use more and more recently and so was wondering if it is a better approach to take. – mrwooster Mar 03 '11 at 10:48
  • There is no other way. Using prototype is the most efficient one. What you have seen might have been special cases, where other requirements had to be fulfilled. But without knowing them it is hard to tell what others did... – Felix Kling Mar 03 '11 at 10:53

7 Answers7

48

This is not "JSON notation", this is JavaScript object literal notation. JSON is only a subset of JS object literal notation, but apart from looking similar, they have nothing in common. JSON is used as data exchange format, like XML.

It is not possible what you want to do.

var myObject = {};

creates already an object. There is nothing what you can instantiate.

You can however create a constructor function and add the methods to its prototype:

function MyObject(arg1, arg2) {
    // this refers to the new instance
    this.arg1 = arg1;
    this.arg2 = arg2;

    // you can also call methods
    this.funca(arg1);
}

MyObject.prototype = {
 funca : function() {
  // can access `this.arg1`, `this.arg2`
 },

 funcb : function() {
  // can access `this.arg1`, `this.arg2`
 }
};

Every object you instantiate with new MyObject() will inherit the properties of the prototype (actually, the instances just get a reference to the prototype object).

More about JavaScript objects and inheritance:


Update2:

If you have to instantiate many objects of the same kind, then use a constructor function + prototype. If you only need one object (like a singleton) then there is no need to use a constructor function (most of the time). You can directly use object literal notation to create that object.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 1
    +1, but maybe write a small example to show the OP the a good pattern of usage. – Martin Jespersen Mar 03 '11 at 10:21
  • Oops, quite right... I have edited my post to say object literal instead of JSON. Thanks. – mrwooster Mar 03 '11 at 10:25
  • Re: Update, see my comment on my OP, this is not about returning objects, but looking for the best way to define the structure of my objects. – mrwooster Mar 03 '11 at 10:35
  • Re: Update2... this is exactly what I am looking for. Best of both world. Thanks. – mrwooster Mar 03 '11 at 10:51
  • you shouldn't overwrite the prototype directly. You can add to it like this: MyObject.prototype.funca = function() { // do stuff }; MyObject.prototype.funcb = function() { // do stuff }; – Jeff Dec 03 '13 at 20:03
  • we can actually create objects created using literal notations. how about - Object.create({ name: 'x', xxx: function(){} }) – rajesh_kw Feb 26 '15 at 10:25
  • can we add object literal in constructor function like `var MyHashSet = function() { this.hash_map = { }; };` – vishal Aug 30 '22 at 04:04
  • @vishal: You can use object literals everywhere you can use an expression. – Felix Kling Sep 01 '22 at 08:59
  • @FelixKling see I use this in problem constructor function MyHashSet, ```https://leetcode.com/submissions/detail/786833822/``` . why we can use object literal everywhere – vishal Sep 01 '22 at 10:39
7

Make the object a function, like this:

var myObject = function(arg1){
  this.funca = function(){
    //...
  };
  this.funcb = function(){
    //...
  };
  this.constructor = function(obj){
    alert('constructor! I can now use the arg: ' + obj.name);
  };
  this.constructor(arg1);
};

// Use the object, passing in an initializer:
var myVar = new myObject({ name: 'Doug'});
Simeon
  • 5,519
  • 3
  • 29
  • 51
  • You are not passing JSON. You are passing a JavaScript object. – Felix Kling Mar 03 '11 at 10:21
  • 2
    The JSON specification is designed to be interchangeable with JavaScript objects. Did you know that JSON is an acronym for **JavaScript Object Notation**? Wiki's first paragraph sums it up well: http://en.wikipedia.org/wiki/JSON – Simeon Mar 03 '11 at 11:33
  • 1
    That does not change anything of that fact that `JSON !== JavaScript object`. JSON **is not** JavaScript and JavaScript **is not** JSON. You can use JSON in much more environments than you can use JavaScript. A JavaScript object is just the representation of JSON in JavaScript. In Java or Python it would be a Java or Python object. http://json.org says: *JSON is a text format that is completely language independent (...)*. In your example, `{ name: 'Doug' }` is (a) not text and (b) does not even conform to the JSON syntax. It would be JSON, if you pass the string `'{"name": "Doug"}'`. – Felix Kling Mar 03 '11 at 16:55
  • 1
    I could have added an eval, and there you had it. But okay then, I changed my answer to exclude the JSON derived object. However, I think that my answer was spot on what he was looking for (before you edited yours to include the same constructor logic as mine). The JSON was not the main issue (and the current edited question doesn't even include the word). – Simeon Mar 03 '11 at 19:13
  • 3
    I did not say that your answer was wrong. But it is important to use the right terminology and to make people aware that JSON is not JavaScript object literal syntax. Using a common (and right) terminology is the key for communication. Anything else creates confusion and misunderstandings... – Felix Kling Mar 03 '11 at 22:49
4
var myObject = function(arg){
    return{
        constructor: function(arg){
            //...
            return this;
        },

        funca: function(){
            //...
        },

        funcb: function(){
            //...
        }
    }.constructor(arg);
};

//...

var myVar = new myObject("...");
  • Great, this is what I was looking for. SO the "secret" is yust creating any method (returning the this) and calling it directly on the literal. – Radon8472 Jun 09 '23 at 12:14
3

Sorry for being late to the party, but... I think saying that this is not possible is a little restrictive depending on how you interpret the OP's question and subsequent comments.

Assuming the OP wanted the namespacing benefits that object literal notation can bring to a library but also wanted to have some "classes" to use within that structure. Could you not use something of this form to combine constructor patterns in to an object literal notation namespaced library structure?

var myNamespace = {
    aProperty: "A value",

    aMethod: function () { return "A method result"; },

    onePlusOneEquals: function () {
        return new myNamespace.classes.NumberStuff(1, 1).added;
    },

    classes: {
        ClassA: function () {
            this.propertyOne = null;
            this.methodOne = function (param) {
                return "The method was passed " + param;
            }
        },

        NumberStuff: function (argOne, argTwo) {
            this.added      = argOne + argTwo;
            this.subtracted = argOne - argTwo;
        }
    }
};

myNamespace.classes.ClassA.prototype.methodTwo = function () { return "At least this one's not bloating our memory footprint with every ClassA created..."; };

...

var anObj = new myNamespace.classes.ClassA();
alert(anObj.methodOne("the parcel")); // "The method was passed the parcel"
alert(myNamespace.onePlusOneEquals()); //2

They're silly examples, but is there any reason not to do this, or why this isn't valid? It gets rid of the global crowding problem that people usually want to use object literal notation for with libraries.

Klors
  • 2,665
  • 2
  • 25
  • 42
  • good example, I have actually a similar problem but it also involved in-heritage. Do you have any idea on how could be solved? Thanks http://stackoverflow.com/questions/19677089/using-namespacing-with-object-literal-notation-and-inheritage – GibboK Oct 30 '13 at 08:26
  • Thanks. I've just taken a look and glad to see it seems like you've already got your question answered. – Klors Oct 31 '13 at 12:57
2
var myObject = {
 funca : function() {
  //...
 },

 funcb : function() {
  //...
 }
};

you can not create a new object of above this way

var myVar = new myObject(...);

but you can achieve the same with below construct,

var myVar = Object.create(myObject );
rajesh_kw
  • 1,572
  • 16
  • 14
2

The simplest way I know is:

function test(arg1, arg2) {
  var1 = arg1;
  var2 = arg2;
  return {
    var3 : var1, // json can access var1
    var4 : var2 // json can access var2
  };
}

arg1 = 'test';
arg2 = function() {
  window.alert('test')
};
var5 = new test(arg1, arg2);
var5.var4();
Delirium tremens
  • 4,623
  • 9
  • 46
  • 59
0

Well, calling new on the object literal is not possible, as others already answered.

But calling new on the object literal constructor is possible.

var obj = {
  arg1: null,
  arg2: null
};

var obj1 = new obj.constructor();
var obj2 = new obj.constructor();

obj1.arg1 = 1;
obj2.arg1 = 2;

console.log("obj2.arg1:", obj2.arg1);
console.log("obj1.arg1:", obj1.arg1);
OfirD
  • 9,442
  • 5
  • 47
  • 90