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.
Related
I originally had code like that below, where I had nothing in the global namespace and I was able to call functions in obj2 from obj1 and vice versa. And all was well.
(function() {
var obj1 = {
obj1_f1 : function() {
},
obj1_f2 : function() {
obj2.obj2_f1();
}
};
var obj2 = {
obj2_f1 : function() {
obj1.obj1_f1();
},
obj2_f2 : function() {
}
};
$(document).ready(function () {
obj1_f1();
});
})();
But now I need to call a function in the obj1 object from the global context, so I have to introduce a global object:
var com_mycompany_make_sure_unique = new function() {
// use 'this.' so that obj1 is not in the global namespace
this.obj1 = {
obj1_f1 : function() {
},
obj1_f2 : function() {
com_mycompany_make_sure_unique.obj2.obj2_f2();
}
};
this.obj2 = {
obj2_f1 : function() {
com_mycompany_make_sure_unique.obj1.obj1_f1();
},
obj2_f2 : function() {
}
};
$(document).ready(function () {
com_mycompany_make_sure_unique.obj1.obj1_f1();
});
};
but I'm not overly happy with that - I have to prepend all function calls with my global object name when calling functions across obj1 and obj2. I think I'm missing a trick.
Thanks for any help,
Paul
You can do this (see comments):
var com_mycompany_make_sure_unique = function() {
// Continue using variables as you were before
var obj1 = {
obj1_f1 : function() {
},
obj1_f2 : function() {
obj2.obj2_f2();
}
};
var obj2 = {
obj2_f1 : function() {
obj1.obj1_f1();
},
obj2_f2 : function() {
}
};
$(document).ready(function () {
obj1.obj1_f1();
});
// Return an object that can be used via the `com_mycompany_make_sure_unique` variable
return {
obj1: obj1,
obj2: obj2
};
}();
This is sometimes called the "revealing module pattern" because everything inside the outer anonymous scoping function is private, and then you "reveal" the parts you want to reveal by putting them on the object you return. If you only needed to expose obj1, for instance, and not obj2, you could do that:
return {
obj1: obj1
};
My question, though, is why do you need to call functions from the global context? With modern event handling and Asynchronous Module Definition loaders like RequireJS, the only global you should really require (ugh) is the AMD function(s).
Side note: I replaced your var ... = new function() { ... }; with var ... = function() { ... }(); There's no need to use new here, and doing so can tend to confuse people (and gives the resulting object an extra prototype it doesn't need). But you could use your original form if you like, just change the end to
this.obj1 = obj1;
this.obj2 = obj2;
...rather than returning an object.
What is the proper way to use _self to always have access to an object? Is using _self ok, or is it bad practice?
I want a good way to get at myObject's attributes and methods, even from functions that aren't called in myObject's context. There are solutions like .bind(this), using _self, and jQuery's $.proxy().
For example:
var myObject = {
name: 'Tyrion',
alias: 'imp',
_self: function() {
return this;
},
// I know this context is fine, but let's pretend it's being called from elsewhere.
getAlias: function() {
var _self = myObject._self();
return _self.alias;
}
}
In order to do what you're looking to do, you'd have to change a few things. #elclanrs is right about what your this context is. I'll put two options below.
var myObject = {
name: 'Tyrion',
alias: 'imp',
// I know this context is fine, but let's pretend it's being called from elsewhere.
getAlias: function() {
// you'd have to do this in every method.
var _self = this;
return _self.alias;
}
}
The other option is a bit different, and not as usable, but I'm adding it so you can see it:
var myObject = function() {
var _self = this;
_self.name = 'Tyrion';
_self.alias = 'imp';
_self.getAlias = function() {
return _self.alias;
};
};
in the second instance, getAlias would be better as a prototype method, but you won't have access to the _self variable, only this.
You can do this too but I wouldn't necessarily recommend it.
var obj = {
_self: this.obj, // if you don't have .obj it points to window
thing: 'thingy',
alsoThis: function() {
return 'another thing'
}
};
obj._self;
It's also possible that since it's not within a closure, or a function in otherwords, the context of this._self may be incorrect if the context is changed by the scope which its referenced in.
Generally, I just do var _self = this; right before a function which I am nesting another function within that requires the this context outside the parent function since the nested one will not have the ability to access the value of this being nested.
That's usually not too common in my experience, and you really shouldn't be declaring such a property/var that needs to be used for the purpose that _self vars serve. It isn't a good practice and would be best to not do.
What if you run into a situation where you need to have _self = some other context?
thisis determined by the invocation of the function. (aka, the way the function is called) See my other answers for more details.
var myObject = {
name: 'Tyrion',
alias: 'imp',
_self: function () {
return this;
},
// I know this context is fine, but let's pretend it's being called from elsewhere.
getAlias: function () {
var _self = myObject._self();
return _self.alias;
}
};
//member invocation
console.log(myObject._self() === myObject); // true
var aFucntion = myObject._self;
//functional invocation
console.log(aFucntion() === myObject); // false
console.log(aFucntion() === this); //true
Instead of worrying about the context of this, a workaround is to assign this to a value in an outer function, and then access that value in an inner functions. This is called closure
var MyObject = function (title) {
var _self = this,
helper = function () {
return _self.title + " " + _self.name;
};
this.title = title;
this.fullName = function () {
return helper(); //functional invocation
//if helper used this, this would be global
};
this.name = 'Tyrion';
this.alias = 'imp';
this.getAlias = function () {
//access to _self through closure
return _self.alias;
};
};
//constructor invocation
var aObject = new MyObject("Mr.");
console.log(aObject.getAlias()); //imp
console.log(aObject.fullName()); //Mr. Tyrion
FYI:
If _self returns myObject, context would not mater.
_self: function () {
return myObject;
}
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();
});
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.
Consider the following code:
(function() {
var a = 5;
var someFunc = function() { ... };
function anotherFunc() {
...
};
})();
window.myGlobalObj = {
init: function() {
// and somehow here I want to access to the IIFE context
}
};
I want to have the execution context of IIFE in my global object. I do have access to function expression and object itself so I can pass or modify something to make it work (and no, I can't rewrite everything inside the object or function).
Is it even possible?
The only way I see how that's poosible is by using eval to simulate dynamic scopes. Do this (note that the IIFE must be placed after the global object):
window.myGlobalObj = {
init: function() {
// and somehow here I want to access to the IIFE context
}
};
(function() {
var a = 5;
var someFunc = function() { ... };
function anotherFunc() {
...
};
eval("(" + String(window.myGlobalObj.init) + ")").call(window.myGlobalObj);
})();
Here's a reference as on how to use dynamic scopes: Is it possible to achieve dynamic scoping in JavaScript without resorting to eval?
Edit: I've included an example to demonstrate the power of using dynamic scopes in JavaScript. You can play with the fiddle too.
var o = {
init: function () {
alert(a + b === this.x); // alerts true
},
x: 5
};
(function () {
var a = 2;
var b = 3;
eval("(" + String(o.init) + ")").call(o);
}());
The "contents" of your IIFE, i.e., a, someFunc, etc., are local to that function scope, so you can only access them within that scope. But you can assign window.myGlobalObj inside the IIFE:
(function() {
var a = 5;
var someFunc = function() { ... };
function anotherFunc() {
...
};
window.myGlobalObj = {
init: function() {
// and somehow here I want to access to the IIFE context
}
};
})();
Then the init function will have access to those variables since they are in its containing scope.
EDIT: if you can't move the definition of myGlobalObj into the IIFE the only thing I can think of is to use the IIFE to create a second global object that you access from myGlobalObj:
(function() {
var a = 5;
var someFunc = function() { ... };
function anotherFunc() {
...
};
// create a global object that reveals only the parts that you want
// to be public
window.mySecondObject = {
someFunc : someFunc,
anotherFunc : anotherFunc
};
})();
window.myGlobalObj = {
init: function() {
window.mySecondObject.someFunc();
}
};
No. It is not possible. The context you want to access is called closure and can be accessed only within the function (in your case, the anonymous function (IIFE how you call it)). For more about closures follow the excellent Douglas Crockfords The Javascript programming language video tutorial.
You will have to place those attributes to some shared object.