Javascript "this" scope - javascript

I am writing some JavaScript code. I am a little confused about this keyword. How do I access logger variable in the dataReceivedHandler function?
MyClass: {
logger: null,
init: function() {
logger = LogFactory.getLogger();
},
loadData: function() {
var dataReceivedHandler = function() {
// how to access the logger variable here?
}
// more stuff
}
};

You can do something like this inside the loadData function to access your object...
MyClass: {
logger: null,
init: function() {
this.logger = LogFactory.getLogger();
},
loadData: function() {
var self = this;
var dataReceivedHandler = function() {
// how to access the logger variable here?
self.logger.log('something');
}
// more stuff
}
};

Assuming loadData is called like so:
MyClass.loadData();
then:
loadData: function() {
var self = this;
var dataReceivedHandler = function() {
self.logger ...
}
// more stuff
}

Because dataReceivedHandler is an anonymous function this will refer to the window object on the global scope. I think of two way you can bypass that.
a) Create a variable inside loadData to hold it's context then use it inside dataReceivedHandler as such:
loadData: function() {
var self = this;
var dataReceivedHandler = function() {
console.log(self.logger);
}
// more stuff
}
b) Change the context of your anonymous function using apply or call.
loadData: function() {
var dataReceivedHandler = function() {
console.log(this.logger);
}
// more stuff
dataReceivedHandler.call(this); // by passing this as the first argument we make sure the context of the excuted function is our current scope's this
}
I prefer option B due to performance and memory usage optimizations, but both would work just fine.

Related

JS Module Pattern's public method as callback victim. (this-issue)

I spent the better part of the day reading about the module pattern and its 'this' scope. Eventually I found a work-around for my problem, although with a feeling there's a better way of doing things.
The actual code is >200 lines, but I've boiled it down to the following:
objA has a method (publicA) that objB wants invoke by callback. The detail that complicates things is that publicA needs help from publicA_helper to do its job. (http://jsfiddle.net/qwNb6/2/)
var objA = function () {
var privateA = "found";
return {
publicA: function () {
console.log("privateA is " + this.publicA_helper());
},
publicA_helper: function () {
return privateA;
}
};
}();
var objB = function () {
return {
callback: function (callback) {
callback();
}
}
}();
objA.publicA(); // privateA is found
objB.callback(objA.publicA); // TypeError: Object [object global]
Fair enough – I've grasped that the caller's context tends to influence the value of 'this'. So I add measures to retain 'this' inside objA, of which none seems to work. I've tried the
var objA = (){}.call({}) thingy, setting var self = this; (calling self.publicA_helper() accordingly). No luck.
Eventually, I added a private variable var self;, along with a public method:
init: function() {self = this;},
...and by making sure I call objA.init(); before passing objA.publicA to objB.callback, things actually work.
I cannot stress the immensity of the feeling that there's a better way of doing this. What am I missing?
The generalized solution is extremely simple.
Write all the module's methods as private, then expose those that need to be public.
I write all my modules this way :
var objA = function () {
var privateA = "found";
var A = function () {
console.log("privateA is " + A_helper());
},
var A_helper = function () {
return privateA;
}
return {
publicA: A
//A_helper need not be exposed
};
}();
Thus, all methods are in the same scope, each one having direct access to all other methods in the same module, and the ambiguous this prefix is avoided.
objB.callback(objA.publicA); will now work as expected.
See fiddle
I've tried the var objA = (){}.call({}) thingy,
How? You want to use call on the callback that you want to invoke with a custom this, not on your module closure. It should be
var objB = {
callback: function (callback, context) {
callback.call(context);
}
};
objB.callback(objA.publicA, objA);
I've tried setting var self = this;
The self variable is supposed to be in a closure and point to the object on the methods are stored. That is only this when your module IEFE would be invoked on your module - it's not. Or if it was a constructor - it's not. You could change that with call as above:
var objA = function () {
var privateA = "found",
self = this;
this.publicA = function () {
console.log("privateA is " + self.publicA_helper());
};
this.publicA_helper = function () {
return privateA;
};
return this;
}.call({});
But that's ugly. In your case, the self variable simply needs to point to the object literal which you're returning as your module:
var objA = function () {
var privateA = "found",
self;
return self = {
publicA: function () {
console.log("privateA is " + self.publicA_helper());
},
publicA_helper: function () {
return privateA;
}
};
}();
Btw, since you're creating a singleton you don't need an explicit self, you could just reference the variable that contains your module (as long as that doesn't change):
var objA = function () {
var privateA = "found";
return {
publicA: function () {
console.log("privateA is " + objA.publicA_helper());
},
publicA_helper: function () {
return privateA;
}
};
}();
Another method would be to simply make all functions private and then expose some of them - by referencing them local-scoped you will have no troubles.
var objA = function () {
var privateA = "found";
function publicA() {
console.log("privateA is " + helper());
}
function helper() {
return privateA;
}
return self = {
publicA: publicA,
publicA_helper: helper // remove that line if you don't need to expose it
};
}();
The reason is that the context is getting changed when you are invoking the callback. Not a generalized solution, but shows that the code works by specifying the context while invoking callback.
var objA = function () {
var privateA = "found";
return {
publicA: function () {
console.log("privateA is " + this.publicA_helper());
},
publicA_helper: function () {
return privateA;
}
};
}();
var objB = function () {
return {
callback: function (callback) {
callback.call(objA);
}
}
}();
objA.publicA(); // privateA is found
objB.callback(objA.publicA); // privateA is found

Add function to object

I have the following code
var PROMO = PROMO || {};
PROMO.Base = (function () {
var _self = this;
var Init = function () {
WireEvents();
};
var WireEvents = function () {
//wire up events
};
} ());
In the same file I have the code to call the above function
I am trying to get to an end point where I can use the following code
$(document).ready(function () {
PROMO.Base.Init();
});
this gives the error
Cannot call method 'Init' of undefined
Now I know there are many ways to write javascript, but in this case I want to be able to call my functions, or least the Init method in the way shown above.
var PROMO = PROMO || {};
PROMO.Base = (function () {
var _self = this;
var Init = function () {
WireEvents();
};
var WireEvents = function () {
//wire up events
};
var reveal = {
Init: Init
};
return reveal;
} ());
You need to return the public facing functions. See updated code.
Working fiddle with both patterns, using IIFE and direct attribution.
Using var makes the definition private and your function is returning nothing. Use this:
PROMO.Base = {
Init: function() {
},
WireEvents: function() {
};
};
You are wrapping the definition with an IIFE(Immediately Executed Function Expression). So your PROMO.Base object will be assigned the value of that (function(){//blabla})(); returns. But your function doesn't have a return statement. By default it will return undefined.
Which is way your PROMO.Base will be undefined and you get this:
Cannot call method 'Init' of undefined
If you really want that IIFE:
var PROMO = PROMO || {};
// NEVER use _self = this inside static functions, it's very dangerous.
// Can also be very misleading, since the this object doesn't point to the same reference.
// It can be easily changed with Function.prototype.call and Function.prototype.apply
PROMO.Base = (function () {
_PROMO = {
Init : function () {
document.body.innerHTML += "itworks";
},
WireEvents : function () {
//wire up events
}
}
return _PROMO;
} ());
PROMO.Base.Init();
Update
The better and easier pattern is to simply assign the functions to PROMO.Base. Dully note you should not capitalize static functions, but only constructors. So if something is not meant to be instantiated, don't call it Init, it should be init. That is the convention.
var PROMO = {};
PROMO.Base = {};
PROMO.Base.init = function() {
console.log("this works");
};
PROMO.Base.wireEvents = function() {
console.log("this is a static function too");
};
You can attach it to the window object like ...
window.PROMO = (function($, _){
// this will access PROMO.Base
PROMO.Base = {
// inner functions here
Init:{}
};
})(jQuery, _);
Then load it as you do.
Or if you depend from jQuery
(function($){
var PROMO = {
// inner functions
Init: function(){},
WireEvents: function(){}
};
$.PROMO = PROMO;
})(jQuery);
On DOM ready
jQuery(function ($) {
var promo = $.PROMO || undefined;
promo.Base.Init();
});

How to get the value of variable in different javascript file?

test.html
<script src="jsv/test1.js"></script>
<script src="jsv3/test2.js"></script>
test1.js:
(function ($) {
var settings = {
taphold_threshold: 750,
hold_timer: null,
tap_timer: null
};)
};
test2.js:
var Navigation = {
init: function () {
self = this;
$('#button').live(tapMode, function () {
alert(settings[taphold_threshold]);
});
}
}
I would like to get the value of settings : taphold_threshold, but it seems i can not get the value by simply alert it. test2.js is the caller and test1.js is callee. It should be some scope problem. How to get the value (750) ? Thanks
The problem is indeed scope - settings will be in an anonymous scope which is not available outside of the closure.
You could change test1 to have a sort of "namespace" - say something like global (although I would personally use a more descriptive name than global).
var global = {};
global.settings = {
taphold_threshold: 750,
hold_timer: null,
tap_timer: null
};
The from test2 you can use:
alert(global.settings.taphold_threshold);
Your code hints at a namespace pattern but falls slightly short.
You might like to consider something like this
var TAP = (function($) {//functional namespace
var settings = {
hold_threshold: 750,
hold_timer: null,
timer: null
};
var setSettings = function(s) {
settings = $.extend(settings, s);
};
var getSettings = function() {
return settings;
};
return {
set: setSettings,
get: getSettings
};
})(jQuery);
Thus, TAP has private member settings and public members set() and get(). You will see that further private and public members are easily added.
Now you have a mechanism to both set and get TAP settings from anywhere that TAP is within scope:
TAP.set({hold_threshold: 500});
var Navigation = {
init: function () {
self = this;
$('#button').live(tapMode, function () {
alert(settings[TAP.get().hold_threshold]);
});
}
}
With TAP as a member in the global namespace, it's public methods are available in all scopes.
More typically, you will use the MODULE pattern, which puts just one PROJECT member into the global namespace, containing any number of MODULES, each containing any number of functional NAMESPACES, for example :
var MYPROJECT = {};//global
MYPROJECT.MODULE1 = {};
MYPROJECT.MODULE1.TAP= (function($) {
var settings = {
hold_threshold: 750,
hold_timer: null,
timer: null
};
var setSettings = function(s) {
settings = $.extend(settings, s);
};
var getSettings = function() {
return settings;
};
return {
set: setSettings,
get: getSettings
};
})(jQuery);
By convention, MYPROJECT, its MODULES and its functional NAMESPACES are capitalized.
settings is nested within a closure and it cannot be accessed from the outside. One solution is to remove the closure so that it becomes a global object. Another solution is to assign it to the window object, same as making the variable global but this works from inside closures. Here is an example:
(function ($) {
window.my_namespace = window.my_namespace || {};
window.my_namespace.settings = {
taphold_threshold: 750,
hold_timer: null,
tap_timer: null
};
});
var Navigation = {
init: function () {
self = this;
$('#button').live(tapMode, function () {
alert(my_namespace.settings[taphold_threshold]);
});
}
};
Since var settings is defined in test1.js inside a closure, hence the problem exists.
You might want to define the settings variable as
window.settings = ...
OR
window['settings'] = ...
So now settings would be defined as a global variable.

JavaScript, 'this' reference, when calling object function from anchor tag

Here is a simplified version of my JS:
var myObject = function() {
return {
functionOne: function() {
//some other logic here
},
functionTwo: function() {
var self = this;
//some logic here
//then call functionOne
self.functionOne();
}
};
}
Then I have this in the body of my html:
click me
Why do I get the error Uncaught TypeError: Object [some url] has no method 'functionOne', when I click the link?
The error that you're seeing doesn't reflect the example code you've shown.
That said, in the way that you're using the code, you should be able to reduce it to simply:
var myObject = {
functionOne: function() {
},
functionTwo: function() {
this.functionOne();
}
}
Your myObject is a function that needs to be called in order to get that object
click me
Why not just define myObject as an object:
var myObject = {
functionOne: function() {
//some other logic here
},
functionTwo: function() {
var self = this;
//some logic here
//then call functionOne
self.functionOne();
}
};
What you need is for your function to be executed immediately. Further to that your declarion of the self variable leads me to think that you are trying to create a closure so that you can access functionOne from functionTwo. If that is the case then I think the following is what you were after:
var myObject = (function() {
function func1( ) {
}
function func2( ) {
func1();
}
return {
functionOne: func1,
functionTwo: func2
};
}());

Private var inside Javascript literal object

How can I declare a private var inside a literal object? Becasuse I've this code:
var foo = {
self: null,
init: function() {
self = this;
self.doStuff();
},
doStuff: function() {
//stuff here
}
}
This works perfectly, but if I have some objects, the var "self" it will override.. apparently it's a global var.. and I cannot use the restricted word "var" inside this object..
How can I solve this, or make an NameSpace for each object?
Thanks!
You can create a function scope to hide the variable:
var foo = (function() {
var self = null
return {
init: ...,
doStuff: ...
};
})();
Though it is not clear what self is supposed to do here, and how foo is used.
You have to use this:
init: function() {
this.self = this;
this.self.doStuff();
},
edit However, it's still a property of the "foo" object, and it's not super-clear where you're getting instances of "foo" from. In other words, the way your code is written, there's only one object.
Alternatively, you could create your object with a closure:
var foo = function() {
var self = null;
return {
init: function() {
self = this;
self.doStuff();
},
doStuff: function() {
//stuff here
}
};
}();
You are not even using the property that you have created. Instead you create another global variable with the same name. Use the this keyword to access properties:
var foo = {
self: null,
init: function() {
this.self = this;
this.self.doStuff();
},
doStuff: function() {
//stuff here
}
}
(Although saving this in a property is truly pointless...)
If you want a local variable in the object, create a closure for it:
var foo = (function(){
var self = null;
return {
init: function() {
self = this;
self.doStuff();
},
doStuff: function() {
//stuff here
}
};
}());

Categories