Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to preserve javascript "this" context inside singleton pattern?

I have something similar to this:

var a = (function () {

    return {

        b: 1,
        c: function () {

            console.log(this.b);
        }
    };
})();

So,

a.c(); // = 1

But if I do

b = 2;
a.c.apply(this); // = 2

Is it possible to preserve the context of "this" inside "a.c()" without changing (too much) the structure of "a" object? I don't have the control of the function's call, so I'd need a workaround to deal with this inside the object itself.

UPDATE:

To be more specific, this is the structure of my files:

Structure 1 (singleton like pattern):

var a = (function () {

    var _instance;

    function init() {

        return {

            b: 1,
            c: function () {

                console.log(this.b);
            }
        };
    }

    return {

        getInstance: function () {

            if (_instance === undefined) {
                _instance = init();
            }

            return _instance;
        }
    }
})();

Structure 2:

var b = {

    c: 1,
    d: function () {
        console.log(this.c);
    }
};

SOLUTION:

I have implemented a solution based on Mahout's answer, spliting the return statement inside init(), so it remains safe for the object context (and the instance) under any situation.

For singleton pattern:

var a = (function () {

    var _instance,
        self;

    function init() {

        return self = {

            b: 1,
            c: function () {

                console.log(self.b);
            }
        };
    }

    return {

        getInstance: function () {

            if (_instance === undefined) {
                _instance = init();
            }

            return _instance;
        }
    };
})();

For object literal:

var b = (function () {

    var self;

    return self = {
        c: 1,
        d: function () {
            console.log(self.c);
        }
    };
})();

So

a.getInstance().c(); // 1
a.getInstance().c.apply(this); // 1
setTimeout(a.getInstance().c, 1); // 1
$.ajax({ complete: a.getInstance().c }); // 1
like image 962
Luciano Fantuzzi Avatar asked Aug 19 '15 19:08

Luciano Fantuzzi


2 Answers

You can slightly change the way you are returning the object from the anonymous function:

var a = (function () {
   var result = {};
   result.b = 2;
   result.c = function() {
     console.log(result.b);
   };
   return result;
})();

This should have the same effect, however it does remove the use of this.

If you can't afford to change the structure of a this much, then alternately you can (much) more dangerously use:

a.c.apply = function() { // Stops the apply function working on a.c by overriding it
    return a.c();
}

If you choose this though you must be wary that anytime a.c.apply is used it will no longer work 'as expected' - it will fix the issue presented here though.

like image 146
Mahout Avatar answered Sep 27 '22 21:09

Mahout


I made this pen to illustrate the differences,I hope it helps:

http://codepen.io/dieggger/pen/BNgjBa?editors=001

var a = (function () {
    return { b: 1,
             c: function () {
              console.log(this.b);
            }
    };
})();


a.c(); //prints 1


b = 2; // global variable "b" which is being hoisted BTW


// The following will act like this:

//it throws "cannot read property 'apply' from undefined" 
//though it prints "1" since the first part invokes the "c" function 
//inside of the"a" module which has the console.log

a.c().apply(this); 


//In this case "this" is the window object which has the variable "b"
a.c.apply(this); // it'll print 2
like image 30
Diego Montes Avatar answered Sep 27 '22 21:09

Diego Montes