An object that returns the instance itself

Background: My last project cannot use a large library, which upsets me. There are a few things I would like to get from any library, such as the missing addClass , hasClass , removeClass , compatible addEventListener , etc. Therefore, I created a small object that would like to get some opinions at another time, but I have some minor problems with the setup as I would like.

For ease of use, I want the object to return a new instance of itself when created.

Given:

  $ = function() { this.name = "levi"; return this; }; console.log($()); 

We get DOMWindow instead of $ due to the unusual nature of this in JavaScript. What is more strange to me is that console.log(new $().name) Name console.log(new $().name) correctly returns "levi". If this is associated with a window, why did the object get the value correctly? We could just add a new console.log(new $()) and it works. However, I do not want to write new things every time. 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 little unnecessary to wrap the object in a function that creates it. Moreover, the returned object is obj and not $ . Comparative tests will fail.

What other ways can this be done? Is there a more elegant solution? I have no doubt about rethinking my whole process. I find myself pretty good at using JavaScript, but creating new JavaScript is something that I'm very new to.




Does anyone see something 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 seems to work in all my tests.

+11
javascript library-design
May 21 '11 at 10:34 p.m.
source share
4 answers

The easiest way to do what you want is (I think):

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

The fact that just returning this does not create an instance from $ is due to the fact that the this method is created as $ as a regular function: in this case, the value of this points to a global object (in the browser: window , the $() execution call actually matches the window.$() ). This is a fact of javascript life, so to speak. The fact that console.log(new $().name) shows the correct value is that you call the function as a constructor that returns an instance of this constructor (i.e., a new instance of $ ). But console.log($().name) will also print 'levi' because it returns a global object with the name property, i.e. window.name . try $(); console.log(name) $(); console.log(name) , and now you will see that name is a global variable. Therefore, if you do not 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 $ ) inside the constructor function. Using the above method, the instance constructor, regardless of whether it was created with or without new , will always be $

Perhaps you need to rephrase the title of your question: "An object [constructor] that returns an instance of itself

Perhaps this blog post may shed more light.

+4
May 21 '11 at 23:53
source share
— -

The jQuery method is the first tests, if this is a window (called as a function), and if so, it returns a new instance of itself. For example:

 var $ = function() { if(this === window) { return new $(); } this.name = "levi"; return this; }; console.log($()); 

This works because when you usually call the ( func() ) function, this will be the same as the calling this . (Related, but not immaterial: obj.method() will have this be obj ) Since the default scope is window , this inside $ will be window when you call it $() .

When you call a function using new , the following happens: JavaScript creates a new object, and then calls your function with this set to that object.

This solution works because it first checks to see if this window exists, and therefore it was called as $() . If it was called as $() , it will return new $() . Otherwise, it was called using new $() and will work as expected.

+5
May 21 '11 at 10:47
source share
 > $ = function() { > this.name = "levi"; > > return this; }; > > console.log($()); 

We get DOMWindow instead of $

When you call $ as funciton, then this keyword is set by the global object, as it would be for any function called like that.

due to the bizarre nature of this in javascript

Javascript is the * keyword that works as directed. It is different from other languages, but that’s how it works.

Which is weirder for me, console.log ($ (). Name) correctly returns "Levi".

When you call $ as a function, its this keyword is a global object, therefore:

 this.name = 'levi'; 

creates a property of a global object named name with the value 'levi'. It is not strange when you know what is going on .:-)

We could just add a new console.log (new $ ()) and it works.

So it is assumed that constructors should be called in javascript. When a function is called with the name new, its this keyword is set to a new object, so this.name will create a new property for this object. By the way, return this redundant; constructors return this by default.

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

console.log ($ ()); Which gives me what I want, but it seems a little unnecessary to wrap the object inside which creates it. Further, moreover, it is type obj, not type $

Presumably you are using typeof, which can only return one of the values ​​specified by ECMA-262. This short list (which includes an object, number, string, and so on) does not include $.

What other ways can this be done?

You can use the approach you found, a clone of Lasse Reichstein Nielsen (also popularized by Dubos Crockford as “generated”) and a sample module of Richard Kornford. Use Google, there are many, many posts about all of the above.

+4
May 21 '11 at 22:56
source share

Try something like this:

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

Update :

@ Levy Morrison

Edit: does anyone see something wrong in the following solution:

Since you asked: I'm not really in love with document.write .
Try instead:

 var log = function(message){ if(message){ document.body.appendChild(document.createTextNode(message)); } document.body.appendChild(document.createElement('br')); } 
+1
May 21 '11 at 23:04
source share



All Articles