Search code examples
javascriptlibrary-design

An Object that returns an instance of itself


Background: My latest project cannot use a large library, which saddens me. There are a few things that I would like to have from any library such as the missing functions addClass, hasClass, removeClass, compatible addEventListener, etc. So I created a little object which I'd like some opinions on some other time, but I'm having a little bit of trouble setting it up how I'd like.

For convenience of use, I want an object to return a new instance of itself on creation.

Given:

 $ = function() {
    this.name = "levi";

    return this;
};

console.log($());

We get DOMWindow instead of $ because of the quirky nature of this in JavaScript. What's more strange to me is that console.log(new $().name) properly returns "levi". If this is bound to window, why did the object properly get the value?. We could just add new console.log(new $()) and it works. However, I don't want to write new everytime. So I tried:

$ = function() {
    var obj = function() {
        this.name = "levi";
    };

    return new obj();
};

console.log($());

Which gives me what I want, but it seems a bit unnecessary to wrap the object inside of a function which creates it. Further more, the returned object is obj and not $. Comparison tests would fail.

What are some other ways this can be done? Is there a more elegant solution? I have no qualms about rethinking my entire process. I consider myself pretty good at using JavaScript, but creating new JavaScript is something I am very new to.


Does anyone see anything wrong with the following solution?

$a = function() {};

$ = function() {
    if (!(this instanceof $)) {
        return new $();
    }

    this.name = "levi";

    return this;
};

//helper function
var log = function(message) {
    document.write((message ? message : '') + "<br/>");
};

log("$().name == window.name: " + ($().name == window.name)); //false
log("$().name: " + $().name); //levi
log("window.name: " + window.name); //result

log();

log("$a instanceof $: " + ($a instanceof $)); //false
log("typeof $a: " + (typeof $a)); //function
log("typeof $: " + (typeof $)); //function

It appears to be working in all my tests.


Solution

  • The most simple way to do what you want would be (I think):

    $ = function(){
        if (!(this instanceof $)){
         return new $;
        }
        this.name = 'levi'; 
        return this;
    }
    

    The fact that just returning this doesn't create an instance of $ is because of the way this is created executing $ as a regular function: in that case the value of this points to the global object (within a browser: window, actually calling executing $() is the same as window.$()). It's a fact of javascript life so to speak. The fact that console.log(new $().name) shows the right value is because you call the function as a constructor, which returns an instance of that constructor (i.e. an new instance of $). But console.log($().name) will also print 'levi', because it returns the global object with property name, i.e. window.name. try $(); console.log(name) and you'll see name is a global variable now. So if you don't want to use the new keyword every time, check if your function is called as a regular function, or as a constructor for an instance (=== instanceof $) within the constructor function. With the above method an instances constructor, no matter if it's instantiated with or without new will allways be $

    Maybe you should rephrase the title of your question to: 'An Object [constructor] that returns an instance of itself'

    Maybe this blog entry can shed extra light.