7

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
Luciano Fantuzzi
  • 916
  • 12
  • 23
  • The *similar* code is giving an error: `Uncaught SyntaxError: Unexpected token :`. – Spencer Wieczorek Aug 19 '15 at 19:56
  • 2
    `a.c()` doesn't return a function, so you can't call its `apply` method. Did you mean `a.c.apply(this)`? – Barmar Aug 19 '15 at 19:58
  • @SpencerWieczorek it's just cause the `return` is on a different line – Josh Beam Aug 19 '15 at 19:58
  • possible duplicate of [Preserving a reference to "this" in JavaScript prototype functions](http://stackoverflow.com/questions/2025789/preserving-a-reference-to-this-in-javascript-prototype-functions) – JJJ Aug 19 '15 at 19:59
  • possible duplicate of [How to access the correct `this` / context inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-context-inside-a-callback) – Bergi Aug 19 '15 at 20:16
  • @SpencerWieczorek and barmar Sorry, I wrote it without testing it first. Just updated the code. – Luciano Fantuzzi Aug 19 '15 at 21:13
  • I read all posts I found before posting this question and I think it's not a duplicate. This case is particular because of the object structure and the premise: I can't use .bind() or something else outside the object because I don't have the control of the function's call, and I don't know where should I define a var like "that = this" to be able to invoke it inside the scope of all functions – Luciano Fantuzzi Aug 19 '15 at 21:22
  • @LucianoFantuzzi: You'll need to split the `return` and the object literal, and assign it a name that you can refer to, like in Mahouts answer. Then you can use closures (like he) or `bind` or whatever. A getter for the `c` property might be an alternative, but more cumbersome approach. – Bergi Aug 19 '15 at 21:35

3 Answers3

2

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.

Mahout
  • 414
  • 1
  • 3
  • 12
  • Thanks, it might be a solution and I'll try to apply this tomorrow. a.c.apply was only intended to show a test case. A more close example is when passing the function as a callback in jQuery or js functions like setTimeout() – Luciano Fantuzzi Aug 19 '15 at 21:53
  • In this case use `a.c.bind` to ensure `a` is given the correct `this` context – Mahout Aug 19 '15 at 21:58
  • I can't refactor the entire system (like modifying all object calls of all objects), that's my main problem here. I'm looking for an object oriented solution that works for jQuery callbacks ( like $.ajax({ success: a.c }) ) or setTimeout(a.c, 1) or even call() / apply() since I'm dinamically overwriting some browser functions like "alert" – Luciano Fantuzzi Aug 19 '15 at 22:16
  • Then can you use the first snippet in above answer? The one using `result` – Mahout Aug 20 '15 at 08:07
  • Well, I've updated my question including the solution you provided adapted to my case. Thanks! – Luciano Fantuzzi Aug 20 '15 at 14:32
  • Nice, I quite like the way you have done `return self = {...}`. A neat way to do it without those additional lines. – Mahout Aug 20 '15 at 14:55
  • Sorry, self was in global context there. I've updated the solution again. That happens to me for always trying to simplify everything :( – Luciano Fantuzzi Aug 20 '15 at 14:58
1

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
Diego Montes
  • 43
  • 1
  • 6
  • The idea is to preserve the singleton pattern (I've updated the question) and the object notation since it's a complex env and I can't refactor everything to deal with this. My workaround now (horripilant) is to call "objectName.getInstance().functionName()" or simply "objectName.functionName()" inside the function scope, same for returning values (for the chainable pattern). – Luciano Fantuzzi Aug 19 '15 at 21:45
0

You can do this:

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

var decorator = function() { return a; };

var b = 2;

decorator.call(this).c(); // => 1

Basically it looks like you want to bind the IIFE, and not the object that it returns, to the outside scope, so that the nested returned object preserves the value of the interior b.

Josh Beam
  • 19,292
  • 3
  • 45
  • 68
  • Thanks. There's a syntax error in "function decorator = function.." (you can remove "function" at the beginning). BTW the ".apply()" was only to demonstrate the scope change, but this way I won't be able anymore to call like "a.c()" or "decorator.c()". Moreover, things like "setTimeout(decorator.call(this).c, 1)" will be still accessing the global scope. – Luciano Fantuzzi Aug 19 '15 at 22:07