3

For example I have this function that builds a Car object.

function Car() {
    var honkCount = 0;
    var honkHorn = function () {
        honkCount++;
        $results.html('HONK!<br />');
    };
    return {
        get honkCount() {
            return honkCount;
        },
        honk: honkHorn
    }
}

Both var car = new Car(); and var car = Car(); don't seem to make much difference and I'm confusing myself a bit.

8
  • 2
    that's not a constructor, constructors use this.something to affect the returned object when call with a new prefix. Commented Jun 20, 2013 at 22:40
  • 1
    looks like a Factory pattern you have there Commented Jun 20, 2013 at 22:41
  • 1
    stackoverflow.com/questions/1646698/… Commented Jun 20, 2013 at 22:41
  • 2
    To answer briefly: if you return anything from your "constructor" (except for this), it stops being a constructor (or being useful as one). As you noticed, using new or not won't make much difference. Commented Jun 20, 2013 at 22:43
  • 1
    @BenjaminGruenbaum: no, right below it says "The value of a constructor’s “prototype” property is a prototype object that is used to implement inheritance and shared properties", which does not apply if you return a literal. [].slice() also gives you a whole new Array object, but nobody considers it a constructor. just because it has two wheels doesn't make it a harley... Commented Jun 21, 2013 at 0:12

3 Answers 3

8

The short answer

There is no big difference between using the new operator and dropping it when you're returning an object.

Quoting "JavaScript Garden":

If the function that was called has no explicit return statement, then it implicitly returns the value of this - the new object. In case of an explicit return statement, the function returns the value specified by that statement, but only if the return value is an Object.

The language specification tells us this:

If Type(result) is Object then return result.

Return obj.

In the [[construct]] algorithm that specifies how constructors are done.


A short dive into the language specification

However, for you ambitious types - Let's explore why in the language specification together! How could we have figured it out on our own?

Here is why, we're evaluating new NewExpression where newExpression is your function. I got there by checking what the new keyword does in the index.

First:

  1. Let ref be the result of evaluating NewExpression.

This is the function call

Then:

  1. Let constructor be GetValue(ref).

Which inside GetValue goes to:

Return the result of calling the GetBindingValue (see 10.2.1) concrete method of base passing GetReferencedName(V) and IsStrictReference(V) as arguments.

This returns the function itself (based on this)

If Type(constructor) is not Object, throw a TypeError exception.

Functions are objects in JS, so it's all good.

If constructor does not implement the [[Construct]] internal method, throw a TypeError exception.

This checks if it's a function. All function have a construct method (looking at a function as a constructor, you can try evaluating (function(){}).constructor and see.

Return the result of calling the [[Construct]] internal method on constructor, providing no arguments (that is, an empty list of arguments).

Great! Let's see what [[construct]] does. It's defined in 13.3.2, and it says a bunch of things. The Jackpot is this:

Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list passed into [[Construct]] as args.

If Type(result) is Object then return result. Return obj.

Ding Ding Ding!

So internally, the spec says that if your function returns an object, the constructor returns it instead of the object created.

Note (One very minor difference is that when you're not in strict mode, using new might catch a bug )


Bonus: Here is a nice explanation on constructors from JavaScript garden

Sign up to request clarification or add additional context in comments.

1 Comment

(Hint, I have a tiny lie to remove another move (parsing the arguments in the function), since you're calling your constructor with an argument list (empty in your case) it evaluates as MemberExpression Arguments and not a ` NewExpression.`, other than that it's exactly the same :))
4

Both var car = new Car(); and var car = Car(); don't seem to make much difference and I'm confusing myself a bit.

You are right, they are both the same, simply because you are returning an object from the function. From the MDN documentation:

3. The object returned by the constructor function becomes the result of the whole new expression. If the constructor function doesn't explicitly return an object, the object created in step 1 is used instead. (Normally constructors don't return a value, but they can choose to do so if they want to override the normal object creation process.)

And as the documentation also says, usually constructor functions don't return a value explicitly.

8 Comments

I think it's called as singleton paattern !
@millimoose, I think I'm right, look at this article and here.
@SheikhHeera—but with this "pattern" you can create multiple instances. The module pattern is more like a singleton pattern.
Thanks, i was confused but can you name this (OP's question) as a pattern ?
@BenjaminGruenbaum the module example is exactly the same as singleton.. the singleton example is just a convoluted way to do the same.
|
0

Someone copied a trivial example of Closure in javascript. The reason to use new with the function is to create a closure, but this is a useless example. Here is a better way to use the Closure. Notice that honkCount is only accesible to the two methods honkHorn and get_honkCount:

function Car() {
    var honkCount = 0;
    this.honkHorn = function () {
        honkCount++;
        $results.html('HONK!<br />');
    };
    this.get_honkCount = function() {
            return honkCount;
    };
}

var car1 = new Car();
var car2 = new Car();
car1.honkHorn();
car1.honkHorn();
car2.honkHorn();

alert('car1 honks: ' + car1.get_honkCount());
alert('car2 honks: ' + car2.get_honkCount());

So now you see that the two objects are tracking there honks distinctly as instantiated private variables thanks to the new and the javascript closure of honkCount.

6 Comments

There is no advantage to this over what the OP is doing though. If you place methods on the prototype you get inheritance, generic methods and don't have to allocate N*2 (function object + prototype object, also a pointer to the function object) extra objects every time you create an instance.
Yes, you are correct, I am creating two more function objects per instantiation, but I use a lot of prototype inheritance in my code. So my way is an advantage in that situation. If you don't care about inheritance and are just wanting to add event handlers with a private variable then the OP way is perfectly fine. Remember though that the function Car cannot be garbage collected in either case event if there is no closure referenced by the methods.
In this code you are not using prototypal inheritance and it cannot be used with prototypal inheritance. The OPs code already creates objects with distinct closure contexts, that's why there is no advantage compared to what OP is doing.
I am not using prototype inheritance in this code but suppose you did the following: function Bus() {} jQuery.extend(true,Bus.prototype,new Car()); Now I can do something interesting: var myBus = new Bus(); myBus.honkHorn();
My confusion was actually coming trying to define getters and setters on objects created by a constructor function. They are easy to define within an object literal so I was returning an object literal with those defined. I realize now that by returning an object from the constructor it renders the constructor pointless and the new keyword makes no difference at that point. I understand closures just fine, I was just getting tripped up with creating properties and just generally confusing myself. I'm all good now though. Thanks for your answer :)
|

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.