6

The first couple paragraphs describe what I'm trying to achieve, the actual question is at the end. Thanks

Previously, I've simply been using new keyword to create objects, and prototypes to assign methods and handle inheritance. Recently, however, (partially inspired by CoffeeScript-generated JS) I decided to play with an object-creating function that would look something like this:

var Test = function(a) {
    function Test(a) {
        this.a = a;
    }
    var numCalls = 0;
    Test.prototype.output = function() {
        alert('I was initialized with ' + this.a);
        numCalls++;
    };
    Test.prototype.called = function() {
        alert('You called the output ' + numCalls + ' times');
    };
    return new Test(a);
};

I would then create a new object like this:

var obj = Test('string');

This approach has a couple of advantages over the typical approach that uses new for every instance. First, I'm much less likely to forget using the word new (I know there are other ways of avoiding new, but from what I've seen they have similar problems to the one I describe below), and second, I can easily see 'private' variables that constructor sees in any function that's now part of the class. I did run into a caveat when testing it though. instanceof no longer works because it doesn't see the inner-scoped object Test. To work around that, I tried to use constructor property instead:

var a = Test('one');
var b = Test('two');
a.constructor == b.constructor              // false
a.constructor.name == b.constructor.name    // true

And this is what got me confused. Creating the same objects without using an object-creating function would cause their constructor to be equal, but in this case they differ. My guess is that what's happening is that a brand new object type gets generated every time the function runs (the object structure is the same, but the instance of the prototype is different).

If my understanding of the problem is correct, does that also mean that the code is allocating additional memory per object instance to my JavaScript for functions that should be shared between instances by tricking it to create an identical object prototype for each instance (defeating the entire purpose of using prototype)? If so, is there a good way to avoid this while still keeping the benefits of this pattern (ability to share private variables between internal functions and not having to use new keyword)?

If I'm misunderstanding the problem, can someone enlighten me on what's actually happening?

4
  • Why do you forget the keyword new? I mean, everytime you create a new object, you use "new", that's very simple. This solution exists for dozens of years in many OO languages, and it's a great thing, because everybody can read and understand your code. While nobody understands var obj = Test('string'), your code will be less readable. But that's the final goal of some JS programmers, I know. ;) Commented Feb 2, 2013 at 23:37
  • @Marcus, as mentioned, the new keyword is not the only advantage to this pattern. Also, I'm not the only programmer on the team. Assuming that everyone will always remember to use the new keyword, while also writing the back-end in Python (which doesn't use new for classes) is unrealistic. It's much easier to spot a missing new at the end of class declaration than at every instance creation. Commented Feb 2, 2013 at 23:55
  • Ah, I got it now: There's no compiler telling the programmer that he forgot the "new", so that's why you don't want to use it. Commented Feb 3, 2013 at 5:07
  • As far as I can see this is rather an issue in IE?! Commented Feb 3, 2013 at 9:16

2 Answers 2

3

If so, is there a good way to avoid this while still keeping the benefits of this pattern...

Try using a module approach instead:

var Test = (function TestModule() {

  function Test(a) {
    this.a = a;
  }

  Test.prototype = {

  };

  return function(a) {
    return new Test(a);
  };

}());

var a = Test('foo');
var b = Test('baz');

a.constructor == b.constructor; // true
a.constructor.name == b.constructor.name; // true
Sign up to request clarification or add additional context in comments.

2 Comments

What does this first ( mean in the line var Test = (function TestModule() {? I really don't understand that kind of code...
@Marcus: It's an IIFE. Take a look here benalman.com/news/2010/11/…
0

Now I searched hard to get this done: The perfect class with full encapsulation and no "new" needed to instantiate it. After searching a while I came up with this:

function Test(x){

    var innerFunction = function(y){
        var variable = y;

        this.getA = function(){
            return variable;
        }

        this.setA = function(x){
            variable = x;
        }
    }
    return new innerFunction(x);
}

But the test results proved it wrong:

var a = Test("foo");
var b = Test("baz");

alert(a.constructor == b.constructor);             //false, not good!
alert(a.constructor.name == b.constructor.name);   //true

So there seemed to be the wrong scope, so I used a public inner function:

function Test(x){

    function innerFunction(y){
        var variable = y;

        this.getA = function(){
            return variable;
        }

        this.setA = function(x){
            variable = x;
        }
    }
    return new innerFunction(x);
}

And running some extensive tests proved that it is correct:

var a = Test("foo");
var b = Test("baz");

alert(a.constructor == b.constructor);             //true, made it!
alert(a.constructor.name == b.constructor.name);   //true

alert(a.getA());                        //"foo" as expected
alert(a.getA() == b.getA());            //false as expected

a.variable = "whatever";
alert(a.getA());                       //"foo" as expected
alert(a.variable);                     //"whatever", doesn't seem preventable

a.setA("somewhere");
alert(a.getA());                       //"somewhere", as expected
alert(a.variable);                     //"whatever", doesn't seem preventable

But, can we use several functions in this way? This was my first approach:

function Test(x){

    function innerFunction(y){
        var variable = y;

        this.getA = function(){
            return variable;
        }

        this.setA = function(x){
            variable = x;
        }
    }
    return new innerFunction(x);
}

function TestToo(x){

    function innerFunction(y){
        var variable = y;

        this.getA = function(){
            return variable;
        }

        this.setA = function(x){
            variable = x;
        }
    }
    return new innerFunction(x);
}

var a = Test("foo");
var b = Test("baz");

var c = TestToo("foo");
var d = TestToo("baz");

alert(a.constructor == b.constructor);             //true, as expected
alert(a.constructor.name == b.constructor.name);   //true, as expected

alert(c.constructor == d.constructor);             //true, as expected
alert(c.constructor.name == d.constructor.name);   //true, as expected

alert(a.constructor == c.constructor);             //false, as expected
alert(a.constructor.name == c.constructor.name);   //true, as NOT expected

So this is it? Do we really always need to know the inner class structure for comparations of a.constructor.name with a string? Nooooo, because in Javascript you can literally do everything (you just need to know how, not why), and I found this final solution:

function Test(x){

    function Test(y){
        var variable = y;

        this.getA = function(){
            return variable;
        }

        this.setA = function(x){
            variable = x;
        }
    }
    return new Test(x);
}

function TestToo(x){

    function TestToo(y){
        var variable = y;

        this.getA = function(){
            return variable;
        }

        this.setA = function(x){
            variable = x;
        }
    }
    return new TestToo(x);
}

var a = Test("foo");
var b = Test("baz");

var c = TestToo("foo");
var d = TestToo("baz");

alert(a.constructor == b.constructor);             //true, as expected
alert(a.constructor.name == b.constructor.name);   //true, as expected

alert(c.constructor == d.constructor);             //true, as expected
alert(c.constructor.name == d.constructor.name);   //true, as expected

alert(a.constructor == c.constructor);             //false, as expected
alert(a.constructor.name == c.constructor.name);   //false, q.e.d.!

I'm serious, I don't know why this works. But it works 100% for sure, with 100% object encapsulation, and 1:1 equality to Java classes. ;-)

2 Comments

This is like using a "Goto": Goto "Test", then create a class named "Test" and instantiate that class. This proves that Javascript functions are no classes per se, only when you call "new" it becomes a class!
Problem: I don't get the same results in IE! The result is: a.constructor == b.constructor // false and a.constructor.name == b.constructor.name // true in IE. But still the functions themselves do work correctly. %D

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.