Search code examples
javascriptnode.jsbrowserify

Object with constructors in javascript


Alright I have a question and I don't know the answer. I am creating an object and the properties of the object are constructor functions. Now within these constructor functions I am referencing this. Would the variable this be referring to the parent object, so if I am creating this.submit in each of the constructors would it be overwriting the previous definition?

The reason I am doing this is because I am looping through each property in my object and if it is a function then I am extending its prototype with Node's Event Emitter so I can dispatch events after successful ajax calls.

Here's an example:

var AccountMethods = function(apiVersion){

    return {
        /** Used to login users
         *
         * @param username - {string} Username
         * @param password - {string} Password
         * @function
         *
         * @example
         * var Login = new Easy.Login('fakeusername', 'fakepassword')
         * Login
         *     .on('done', function(data){
         *         if(data.success){
         *              // user is logged in
         *         } else{
         *             throw new Error(data.error);
         *         }
         *     })
         *     .on('error', function(err){
         *         // do something with err
         *     });
         */
        Login: function (username, password) {
            this.submit = function () {
                $.post(apiVersion + 'account/login', {
                    username: username,
                    password: password
                })
                    .done(function (data) {
                        if(data.success){
                            this.emit('done', data);
                        } else{
                            this.emit('error', data)
                        }
                    }.bind(this))
                    .fail(function (err) {
                        this.emit('error', {
                            error: err.status + ' ' + err.statusText + ' : ' + err.responseText
                        });
                    }.bind(this));
            }
        },
        /** Used to logout users
         *
         * @function
         *
         * @example
         * var Logout = new Easy.Logout();
         *
         * Logout.on('done', function(){
         *     // user is logged out
         * });
         *
         * Logout.submit();
         */
        Logout: function () {
            this.submit = function () {
                $.post(apiVersion + 'account/logout')
                    .done(function (data) {
                        this.emit('done');
                    }.bind(this))
                    .fail(function (err) {
                        this.emit('error', err);
                    }.bind(this));
            }
        },
    }
}

You can see within the comments example usage. This definitely works the way I want it to, however is it right?

I can do this and it works as expected:

var Login = new Easy.Login('fakeusername', 'fakepassword');
Login.submit();

var Logout = new Easy.Logout();
Logout.submit();

Both submit functions are set within the same parent object using this.submit = ... but it works as expected. Can someone explain this to me?


Solution

  • When you're calling a constructor function with new, a new, empty object is bound to it as this, so inside the function this will refer to the new object that is being created (ie. what will be returned from the new Link() call.) That means in your example the variables Login and Logout are different objects, with different submit methods.

    If somebody tried calling Login without new, then the value of this would depend on the calling context: if called as Easy.Login, then this would refer to Easy, but if called as a function reference directly, then the value of this will depend on whether you have strict mode enabled or not. (See Matt Browne's comment below)

    All in all: as long as you're using them only as constructor functions, this approach is correct. But as you see, not calling constructor functions as they were designed to be called can be problematic, hence the convention that constructor functions' names start with capital letters (which you are following correctly), so people don't make the mistake of calling them without new (most static code analyzers will warn you by default for not following this convention).