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

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

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.

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

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.

Related

In JavaScript, How to reference the return value of a method from a deeper nested method

What is the value of this in the two contexts, and how does one nested function reference the other function's returned value?
var rootObj = {
nestedObjA: {
functionA: function() {
// what is value of 'this' here?
return 'foo';
}
},
nestedObjB: {
functionB: function() {
// how to reference? >> rootObj.nestedObjA.functionA
// what is the value of 'this' here?
}
}
}
There is no guaranteed answer to "What is the value of this inside of a function?", as you can set it with the apply()/call() methods of the function prototype.
var rootObj = {
nestedObjA: {
functionA: function() {
var foo = "bar";
return foo;
}
},
nestedObjB: {
functionB: function() {
var foo = this.nestedObjA.functionA();
console.log(foo);
}
}
}
// When calling functionB() you could use:
rootObj.nestedObjB.functionB.apply(rootObj);
The value of this by default is the function scope of functionB;
The value of this within both functions should be rootObj.nestedObjA.functionA , rootObj.nestedObjB.functionB, respectively. To check value of this , could add console.dir(this) within each function.
To return a value from a function you should add return within function body , e.g.; return rootObj.nestedObjA.functionA() withinrootObj.nestedObjB.functionB
var rootObj = {
nestedObjA: {
functionA: function() {
// what is value of 'this' here?
console.dir(this);
return 'foo';
}
},
nestedObjB: {
functionB: function() {
// how to reference? >> rootObj.nestedObjA.functionA
// what is the value of 'this' here?
console.dir(this);
return rootObj.nestedObjA.functionA()
}
}
}
var res = rootObj.nestedObjB.functionB();
console.log(res); // 'foo'

Get name of function inside of object

Here is my code:
var structure = {
A: function() {
this.B();
},
B: function() {
console.log(arguments.callee.caller.toString());
}
};
Then I run:
structure.A();
As you understand, I'm trying to get caller function name, but result is empty string. Are any ideas how to do that?
I've added the function name A, and slightly changed the statement that gets the string to print. Does this do what you want?
var structure = {
A: function A() {
this.B();
},
B: function() {
console.log(arguments.callee.caller.name);
}
}
structure.A();

How to handle this to point to the upper most enclosing fn?

Imagine I have a fn like this:
var test = (function() {
var self = this;
var a = function() { (function() { self.b() })() }
var b = function() { console.log("hello") }
return { a:a,b:b }
})();
test a function calls the b function inside an another function. However I retained the this as self, still I'm getting issue:
test.a()
Uncaught TypeError: self.b is not a function(anonymous function) # VM246:4a # VM246:4(anonymous function) # VM247:2InjectedScript._evaluateOn # (program):883InjectedScript._evaluateAndWrap # (program):816InjectedScript.evaluate # (program):682
Where I'm making mistake? Why the self isn't retaining the this?
How to solve this issue?
Note
Changing the function to this:
var a = function() { (function() { b() })() }
this works. However I don't want to this way, because, imagine now I'm testing the function a calls function b. Now I want to spy b, if I do :
var spyb = sandbox.spy(test,"b")
and call:
test.a()
in the test code if I try to do the assertion:
assert(b.calledOnce)
this fails, as spy lib couldn't able to figure out test b fn is called.
So kindly help me to get the self way.
this within the anonymous function you use to build test is either undefined (in strict mode) or the global object (in loose mode); neither is what you want.
You don't need an object reference at all to call b from a:
var test = (function() {
var a = function() { (function() { b() })() }
// No need for an object -----------^
var b = function() { console.log("hello") }
return { a:a,b:b }
})();
Since a is a closure over the context of the call to the anonymous function, it has an enduring reference to b (as b does to a).
But if you want to use one, you could do this:
var test = (function() {
var obj = {
a: function() { (function() { obj.b() })() },
b: function() { console.log("hello") }
};
return obj;
})();
when you return
{a:a,b:b,c:self}
you would see 'self == window' is true, so the self is point to window.
but declare variables 'b' in function use 'var' , the scope of 'b' is in the function , not the window , so 'self' cannot reach the variables 'b'.
the correct is :
var test = function() {
var self = this;
self.a = function() { (function() { self.b() })() }
self.b = function() { console.log("hello") }
return { a:self.a,b:self.b }
};
var q = new test();
q.a(); //hello

Extending prototype within anonymous function - strange effects

When experimenting with a method to make private static methods, I came across this very strange behavior. In the following code, the public method getData is overwritten by it's own return data, though it is never explicitly called! This is very strange to me and wondered what is going on here. I suppose it serves me right for not just encompassing the entire page in the anonymous function as per the module pattern, but I would still like to understand this bug.
function MyClass() {
this._prop = true;
}
MyClass.prototype.getData = function () {
this._prop = false;
return { a: 2344, b: 765, c: 234 };
}
(function () {
var privateStatic = 0;
MyClass.prototype.getCount = function () {
return privateStatic++;
}
} ());
var m = new MyClass();
console.log(m.getData()); //Error (object is not a function)
console.log(m.getData); //prints {a:2344,b:765,c:234}
The reason for this strange behavior is that getData is being immediately invoked due to the lack of a semicolon after the function declaration (great spot, dandavis) and the IIFE straight after it, wrapped in parens. Essentially, this:
MyClass.prototype.getData = function () {
this._prop = false;
return { a: 2344, b: 765, c: 234 };
} // Notice no semicolon here!
(function () {
var privateStatic = 0;
MyClass.prototype.getCount = function () {
return privateStatic++;
}
} ());
Becomes this:
MyClass.prototype.getData = function () {
this._prop = false;
return { a: 2344, b: 765, c: 234 };
}();
Which is therefore setting the getData property to the return value of the function. Hence why m.getData prints out { a: 2344, b: 765, c: 234 } and m.getData() doesn't work (it's no longer a function!).

Passing variables between two JavaScript modules not working

I have two modules within the same namespace, and I want to pass a variable between them. The namespace is called app, and the variable is a - but for some reason my variable a always comes out null when my method is called.
Here is the code:
// module 1
(function() {
app.module1 = (function() {
var a = null;
canvas.addEventListener('mousedown', function(e) {
a = { message: hallo };
app.module2.print();
}, 0);
return {
a: a
};
})();
})();
// module 2
(function() {
app.module2 = (function() {
var print = function() {
console.log(app.module1.a);
}
return {
print: print
};
})();
})();
Check this jsbin
Basically what you can do is to calculate variable each time:
In your module 1:
a: function(){return a;}
Wherever you are using a:
console.log(app.module1.a());
It's because your handler is referring to the local a and not the a property on the module. I suggest you modify the object instead, or you could do this:
// module 1
(function () {
app.module1 = (function () {
var interface = {
a: null
};
canvas.addEventListener('mousedown', function (e) {
//this way, you are modifying the object
interface.a = {
message: hallo
};
app.module2.print();
}, 0);
return interface;
})();
})();

Categories