16

Let us say I have the following object constructor:

function Foo(bar) {
    this.bar = bar;
}

If I run the function in the global scope without the new keyword then bar will be set in whatever scope Foo() is called in:

var foo = Foo(42);
console.log(bar); // 42
console.log(foo.bar); // ERROR

So my idea is to do something like this:

function Foo(bar) {
    if(!(this instanceof Foo)) {
        // return a Foo object
        return new Foo(bar);
    }
    this.bar = bar;
}

That way if I do new Foo(42) or Foo(42), it would always return a Foo object.

Is this ever a good idea? If so, when? When (and why) would it be wise to avoid this technique?

Shog9
  • 156,901
  • 35
  • 231
  • 235
qwertynl
  • 3,912
  • 1
  • 21
  • 43
  • 1
    Have you ever heard anyone make a case against it? – cookie monster Dec 31 '13 at 16:11
  • @cookiemonster Yes. Something along the lines of "Why would you do that?" "You are **ruining** the sanctity of javascript!" "Why would you force an object on someone when they don't want it?" – qwertynl Dec 31 '13 at 16:13
  • None of those are cases against it. They appear to be the rantings of irrational minds. Have you heard an actual case? I can't imagine why such a guard would be undesirable. – cookie monster Dec 31 '13 at 16:15
  • *"Is this ever a good idea?"* Doesn't your question itself demonstrate when/why it's a good idea? – cookie monster Dec 31 '13 at 16:17
  • 2
    @cookiemonster it is a good "_idea_" in my case, but other people might not agree and I would like to know why and in what case. – qwertynl Dec 31 '13 at 16:17
  • Yes, others may not agree. And unless they can provide a rational reason for their disagreement, does it matter? I'm just trying to figure out the motive for your question. – cookie monster Dec 31 '13 at 16:19
  • @cookiemonster To me? Yes. I **never** use this method. It would make my code quite confusing and I would probably never know what was going on. One time I use `new` and the next I don't __WHHAAAATT__?!?! That makes no sense in my mind. We should have some consistency in our code on what does what where... – qwertynl Dec 31 '13 at 16:21
  • So the reason for your question is that you don't like this approach, and want to start a discussion about it? Of course we should be consistent. That doesn't mean that a particular guard is good or bad. It's just a choice of style. Who cares? What battle are you trying to fight here? – cookie monster Dec 31 '13 at 16:24
  • Yea, but if someone else reads my code won't they become quite confused @Emissary ? – qwertynl Dec 31 '13 at 16:31
  • @qwertynl: Be honest now... is this an extension of some argument you had with another developer? You've clearly formed your personal opinion on the matter, so why are you asking about it? – cookie monster Dec 31 '13 at 16:36
  • @Emissary: Throwing an error is a good alternative. – cookie monster Dec 31 '13 at 16:37
  • Can you add that as an answer @Emissary ? – qwertynl Dec 31 '13 at 16:38
  • @qwertynl: If that's what you want for answers, you should rephrase your question to be less of an opinion poll, and to be more honest saying *"I don't like this approach because of X, Y and Z. What is a good alternative?"* This just isn't a good place for these coding style discussions. – cookie monster Dec 31 '13 at 16:40
  • @cookiemonster I did not realize that I disagreed with my question until these comments came up. I thought about it some more and saw that it seemed silly. [related meta post](http://meta.stackexchange.com/q/214420/241700) – qwertynl Dec 31 '13 at 16:42
  • *"Why would you force an object on someone when they don't want it?"* - this objection doesn't make sense. When you call a function you get back whatever data type the function is designed to return, in this case, an object. If you don't want an object then don't call the function. – nnnnnn Mar 16 '16 at 06:58

4 Answers4

8

While I don't have anything against that style, I would not personally use it just to be consistent. Sure, I can make all my constructors like this but it would seem like a lot more code to write.

If I'm worried about accidentally invoking a constructor without new, I would rather use JSHint to warn me about it. See http://www.jshint.com/docs/options/#newcap.

Chetan S
  • 23,637
  • 2
  • 63
  • 78
8

This can be useful for when you would like to internally construct an object wrapper.

A little known library internally uses this approach, jQuery.

They do not use the instanceof approach any longer though. Every time jQuery is called, it automatically does this:

// Define a local copy of jQuery
jQuery = function( selector, context ) {
 // Need init if jQuery is called (just allow error to be thrown if not included)
 return new jQuery.fn.init( selector, context );
}

Everything it does internally references this local copy. At the very end of its work, it then attaches it to the global scope

window.jQuery = window.$ = jQuery;

So every time you call $() it internally uses new. It is also assuming that you do not use new externally, but it really doesn't care if you do or not.


edit

jsFiddle Demo

//Foo entrance
function Foo(bar){
 //construct a new Foo object to return
 return new Foo.prototype.build(bar);
}

//constructor for returning new prototype
Foo.prototype.build = function(bar){
 //initialize base settings
 this.bar = bar;
 //chain object
 return this;
};

//this is the complex part
//tie the prototype of Foo.prototype.build.prototype to Foo.prototype
//so that is "seems" like Foo is the parent of the prototype assignments
//while allowing for the use of returning a new object in the Foo entrance
Foo.prototype.build.prototype = Foo.prototype;

//simple expansions at this point, nothing looks too different
//makes a function for a constructed Foo that logs hello
Foo.prototype.hello = function(){
 console.log("Hello "+this.bar+" :)");
 //returns this for chaining
 return this;
};

//more extensions, this one changes this.bar's value
Foo.prototype.setBar = function(arg){
 //accesses the current Foo instance's .bar property
 this.bar = arg;
 //returns this for chianing
 return this;
};

//should be seeing a pattern
Foo.prototype.goodbye = function(){
 console.log("Bye "+this.bar+" :(");
 return this;
};

var foo = Foo(42);
//console.log(bar); // ERROR
console.log(foo.bar); // 42
foo.hello(); //Hello 42 :)
foo.hello().setBar(9001).goodbye(); //Hello 42 :) Bye 9001 :(
Foo(12).hello(); //Hello 12 :)
Travis J
  • 81,153
  • 41
  • 202
  • 273
  • So what does `new jQuery(...)` do? – qwertynl Dec 31 '13 at 18:59
  • @qwertynl - It does the same thing as `jQuery(...)`. There is no difference. – Travis J Dec 31 '13 at 19:16
  • So `new (new Foo(...))` does not do anything different from `new Foo(...)`? – qwertynl Dec 31 '13 at 19:22
  • @qwertynl - Well that isn't really the same. When used on a function, `new` causes a Function object to be returned. This object is not a function, so you can't really use `new` on it. If you try, you will get an error: "TypeError: object is not a function". The reason that jQuery appears to get away with it, is that it is *internally* using this new version, so that no matter how it looks or is used externally, internally it will always have access to its prototype. It is also possible to *not* return the Function object. This can be accomplished by returning anything else. – Travis J Dec 31 '13 at 19:47
  • jQuery does this as well. In its init function, it uses: `return jQuery.makeArray(selector, this);` so this is what you have access to when you call `$`. It either returns a constructed jQuery object or if nothing matches the selector, it returns `[]`. Here is a very simple demo of the nested object construction: http://jsfiddle.net/6hw3h/ . It only shows how `new` can appear to be used twice. But really, it is used on separate functions each time. And if an attempt were made on the last one, it would error. – Travis J Dec 31 '13 at 19:47
  • @qwertynl - Please see my extended edit for a more verbose demo of the concept. – Travis J Dec 31 '13 at 20:25
  • But in your example (in your answer) can I still do `new Foo(42)`? – qwertynl Dec 31 '13 at 20:26
  • @qwertynl - Yup. Wouldn't change anything, because the `new` object construction happens internally and that is what is returned. So regardless of requesting a new Function object or not, you get one. That decision is made for you when `return new Foo.prototype.build(bar)` is used. You could also just skip the assignment step, and do something like `Foo(12).hello();`. – Travis J Dec 31 '13 at 20:28
  • So returning from the constructor undoes the `new`? So if I `return bar` instead from the constructor `foo === 42` and **not** `foo === (obj) Foo`? – qwertynl Dec 31 '13 at 20:35
  • 2
    @qwertynl - I think the return value has to be a complex type. If it is a value type then you still get the constructed Function object. So `return bar` or `return "hi"` will still return a new Foo, but `return []`, or `return {}`, or `return new Something()`, or any other complex type will be returned instead. For example: `function Foo(bar){ this.bar = bar; return []; } var foo = new Foo(42); console.log(foo); console.log(foo.bar);`. foo is going to be [] and foo.bar is going to be undefined. Whereas if `Foo` returned `bar`, then foo would be a constructed `Foo` and `foo.bar` would be 42. – Travis J Dec 31 '13 at 20:41
  • Very interesting... And weird. You learn new things every day :-) – qwertynl Dec 31 '13 at 20:44
3

I have one good reason to avoid it: if you're not forgetting to use new, then it's just unnecessary overhead. You may never notice this unless you're creating thousands of instances of a given object, but it's still there.

I recommend limiting the number of places in your code where new-based object creation is done - if you're not relying on it every place you need a new object, then you don't have to worry about forgetting it in one of them. There are numerous patterns that can help you here, but the simplest one is to just make one function somewhere that's responsible for object creation, and defer to that every time - whether it creates 1 instance or 1,000,000 instances is up to you.

That said, if this doesn't make sense then this sort of guard is an excellent fallback. Note that it bears a lot of similarity to how JavaScript's built-in type constructors double as type converters when used without new - so I would consider user-defined types as a particularly apt place to use this technique.

See also: Is JavaScript's "new" keyword considered harmful?

Community
  • 1
  • 1
Shog9
  • 156,901
  • 35
  • 231
  • 235
2

Advocates of "readability" might describe it as an anti-pattern but you know your own code better than anyone else - if it works for you then go for it. Personally I'd rather have one way of doing one thing - it makes it clear and concise. If another developer enters the fray and tries to use my object with out instantiating it I'd rather it throw an error to tell him/her they are being daft.

Emissary
  • 9,954
  • 8
  • 54
  • 65