Search code examples
javascriptobjectprototypetheory

Javascript: creation of object from an already instantiated object versus the prototype


I have a rather academic question that doesn't particularly apply to anything I'm doing, I just really want to know the answer!

Say we have a simple object definition in the global namespace as such:

TestObject = function(){};

It has a method added to it's prototype that can be instantiated into a new object itself:

TestObject.prototype.AnotherObject = function() {};

Instantiate the first object:

var myObject = new TestObject();

Now my question is this:

How does

myObject.myProperty = new myObject.AnotherObject();

differ to

myObject.myProperty = new TestObject.prototype.AnotherObject();

Or are they exactly the same?

The difference I see is this: I could use the second method to instantiate objects within the TestObject context without knowing the name of the instantiated object itself, i.e.

TestObject.prototype.createAnObject = function() {
    this.anotherProperty = new TestObject.prototype.AnotherObject();
}

And finally:

What are the implications of using a prototype method to instantiate an object of the same name? Why do this result in an infinite loop? (What actually happens inside..)

TestObject.prototype.AnotherObject = function () {
    this.AnotherObject = new TestObject.prototype.AnotherObject();
};
myObject.AnotherObject();

Yet this does not...

TestObject.AnotherObject = function() {};

TestObject.prototype.createAnObject = function() {
    this.AnotherObject = new TestObject.prototype.AnotherObject();
};
myObject.createAnObject();

...

I have a deep desire to understand the relationships between objects here! Thank you!

The reason I ask these questions is because I want to make something like so where there is a 1:1 relationship between objects:

ClientObject = function () {
    this.objectname = "a client class";
}

ClientObject.prototype.loginUser = function(name) {
    this.loggedin = true;
    if (typeof this.User === 'undefined') {
        this.User = new ClientObject.User(name);
    }
}
ClientObject.User = function (name) {
    this.username = name;
}

ClientObject.User.prototype.getProfile = function() {
    return 'user profile';
}

var testClient = new ClientObject();

console.log('testClient.User = ' + (typeof testClient.User)); // should not exist
testClient.loginUser('Bob'); // should login 'bob'
console.log('testClient.User = ' + (typeof testClient.User)); // should exist now Bob is logged in
console.log(testClient.User.username); // should be bob
testClient.loginUser('Tom'); // should not do anything as User object already created
console.log(testClient.User.username); // bob still
console.log(testClient.User.getProfile()); // new functionality available

I am just not sure if I'm breaking any best practises or conventions here unwittingly.


Solution

  • myObject.myProperty = new myObject.AnotherObject();

    differ to

    myObject.myProperty = new TestObject.prototype.AnotherObject();

    There's no difference at all. Remember, objects in JavaScript have a prototype chain. When you call new myObject.AnotherObject(); the engine will first check for a AnotherObject on myObject itself. Failing to find it, it will check on myObject's prototype, which it will find. The second version

    myObject.myProperty = new TestObject.prototype.AnotherObject();
    

    Just goes right to the place where AnotherObject is defined.


    TestObject.prototype.AnotherObject = function () {
        this.AnotherObject = new TestObject.prototype.AnotherObject();
    }
    myObject.AnotherObject();
    

    Just walk through the code. When you say: myObject.AnotherObject();, AnotherObject will be called, with this set to myObject. The first line of that will attempt to create a new property on myObject (which is this) by setting it to the result of

    new TestObject.prototype.AnotherObject(); 
    

    which will then re-enter the very same AnotherObject function, but this time with this set to a new object whose prototype is set to TestObject.prototype.AnotherObject's prototype. And so on ad infinitum


    Finally,

    TestObject.prototype.createAnObject = function() {
        this.AnotherObject = new TestObject.prototype.AnotherObject();
    }
    myObject.createAnObject();
    

    Will not cause an infinite loop, so far as I can tell, and as far as I can test: FIDDLE

    Basically, createAnObject will enter with this set to myObject. Inside of which a brand new property called AnotherObject will be created on myObject, which will be set to a new invocation of the AnotherObject function you previously set up.

    Note that after this call is made, the AnotherObject function will still exist, but, it will be shadowed by the AnotherObject property you just created. So now you'll never ever be able to say

    var f = new myObject.AnotherObject()
    

    Because you now have a AnotherObject property sitting right on myObject, which will be found and returned before anything on the prototype is ever checked.

    Well, I mean, you could always say delete myObject.AnotherObject and remove that property from the object, which would then open you up to the AnotherObject being found on the prototype, but really, you should avoid name conflicts like this to begin with.


    Regarding your last bit of code

    A) Why not make User its own function?
    B) Why not set up this.User = new ...() right in the ClientObject constructor function? That way you wouldn't need the undefined check C) ClientObject should be defined as

    function ClientObject(){...` 
    

    the you have it now seems to be creating an implicit global.