I have a script with the following structure:
Test = {
CONSTANTS : {},
VARIABLES : {},
MARKUP : {},
FUNCTIONS : {
init : function () {
// Access variable from different namespace
var all_constants = DifferentNamespace.CONSTANTS; // WORKS
var tester = DifferentNamespace.CONSTANTS.chunk_of_markup; // SAYS UNDEFINED
}
},
init : function () {
// Call real init() function
$(document).ready(function () {
Test.FUNCTIONS.init();
});
}
};
$(document).ready(function () {
Test.init();
});
If I remove either of the $(document).ready(..) function calls, when I try to access a constant from a different namespace it is undefined; with both is works well.
As you can see I'm using two init() functions, one it just to neaten up the call to init because I have wrapped functions inside an additional object.
If I remove the function that is on the same level as CONSTANTS, VARIABLES etc and try to call the init() within Test.FUNCTIONS it still does not work.
Edit:
If i console.log(all_constants) I get the full object (with .chunk_of_markup) but if I console.log(tester) is get undefined. If i wrap tester i get []
I should also note that the other namespace gets the markup from a seperate file.
Any ideas why?
Having two document ready doesn't make a difference here. You could have one document.ready and/or call Test.FUNCTIONS.init directly and all should work, and the fact that they are in different namespaces doesn't matter as well.
As for why you're getting undefined, I think it is probably because your chunk_of_markup variable is actually undefined at that point. My guess is that you're getting the value for it through AJAX and so the call is done asynchronously which means the DOM will be ready before it actually returns a value. When you use the Debugger then the value is evaluated at the point of time where you run the command so by then, the async call already returns successfully (it's a race condition, if you're fast enough and your AJAX is slow then you can still get undefined, and it's also why 2 ready functions happen to make it slow enough for the AJAX call to return but it's still unreliable).
In all cases, if my theory is correct, then you need to hook to the callback of the AJAX request rather that DOM ready event, this is the only place where you can guarantee that your variable is defined.
Why not call the function init() in the document Handler itself.. I don't think that will lead to the same problems.. You can remove the Test.init() completely as it does not seem to do anything in here
Test = {
CONSTANTS : {},
VARIABLES : {},
MARKUP : {},
FUNCTIONS : {
init : function () {
// Access variable from different namespace
var all_constants = DifferentNamespace.CONSTANTS; // WORKS
var tester = DifferentNamespace.CONSTANTS.chunk_of_markup; // SAYS UNDEFINED
}
}
};
$(document).ready(function () {
Test.FUNCTIONS.init();
});
Related
I am new to Javascript, can someone please help me understand if there is a fundamental difference between these 2 ways
First where I use this to call a function defined inside the var itself
var TxMmm={
name: {},
timeout: 2000,
testFunc1: function(){
console.log("testFunc1");
testFunc2();
this.testFunc3();
},
testFunc3: function(){
console.log("Test func3");
}
}
function testFunc2(){
console.log("This is func2 is outside var");
}
v/s Below where I use the var TxMmm to call function defined inside itself.
var TxMmm={
name: {},
timeout: 2000,
testFunc1: function(){
console.log("testFunc1");
testFunc2();
TxMmm.testFunc3();
},
testFunc3: function(){
console.log("Test func3");
}
}
function testFunc2(){
console.log("This is func2 is outside var");
}
In that specific code there isn't that much difference, because the object is a singleton. Some differences:
In your first code block using this, this could refer to something other than the TxMmm object in (say) testFunc1, depending on how testFunc1 is called. For instance, this would fail:
const fn = TxMmm.testFunc1;
fn();
But in your second code block, TxMmm will refer to the object (unless the next point comes into play), so that would work. More here and here.
If someone does:
const x = TxMmm;
TxMmm = somethingElse;
x.testFunc1();
...your first code block using this will keep working, because this is set by how testFunc1 is called. But your second code block using TxMmm would fail, because TxMmm doesn't point to the object anymore.
It almost never matters (seriously, very nearly never), but in your second code block, when you use TxMmm, the JavaScript engine has to look up that identifer to find it in the enclosing scope. Your code using this is able to resolve it immediately (since those aren't arrow functions), which in very rare situations can be faster in a noticeable way.
For non-singleton objects, this can be very important, since it tells the code which object's properties to use.
I am trying to create a basic javascript framework that you can pass different things into, including functions for it to execute later. Right now, I'm in a more simple testing phase, but I can't quite get the function calling to work. A piece of my code is here:
[My JS Fiddle][1]http://jsfiddle.net/mp243wm6/
My code has an object that holds different data, and I want to call the method later, but with data that is available at the time of creation. Here is a code snippet of the function that uses the function that is passed to the object:
clickMe : function() {
this.obj.click(function() {
this.func();
});
}
Any suggestions or things I should read are welcome.
The problem is that there're two different contexts:
clickMe : function() {
// here is one
this.obj.click(function() {
// here is another
this.func();
});
}
You can simple pass the function as parameter, like the following:
clickMe : function() {
this.obj.click($.proxy(this.func, this));
}
http://jsfiddle.net/mp243wm6/2/
The problem:
Considering your code in the JSFiddle, you have:
onClick : function() {
this.obj.click(function() {
this.func();
});
},
As noted, you have different contexts going on here.
Consider the snippet this.obj.click(function() { this.func(); }). The first this here is a reference to the framework.events object. The second this here is a reference to whatever will be this when this function get called. In the case of your JSFiddle, when this.func gets called, this is actually the DOM object that represents the <div id="test">TEST</div> node. Since it doesn't have a func function, calling func() on it causes:
Uncaught TypeError: undefined is not a function
You have to understand the following: you have to pass the correct this in which you want the function func to be called.
The solution:
A couple of ways to make it work as you would like:
1. with bind
this.obj.click(this.func.bind(this));
This way, you are telling: "call my this.func function, but make sure that it will be called using the this that I am passing as a parameter". Vanilla JS, no $.proxy stuff.
JSFiddle
2. with a copy of the reference to the actual function
onClick : function() {
var theFunctionReference = this.func;
this.obj.click(function() {
theFunctionReference();
});
},
This way, you will not rely on the value of this outside of the context of the framework.events object.
JSFiddle
The issue is that this is not bound to the correct object. I would suggest you look into Function.bind() because that creates a function with this pointing to the right thing.
I'm trying to get better with JavaScript and learn how to utilize my code in functions and keep everything clean. I'm trying to run a function on page-load...
var setColors = function(){
this.init = function(){
$.getJSON('js/colors.json', function(colors) {
$.each(colors, function(i, colors) {
$('<li>', {
text: colors['color'],
'name' : colors['color'],
'data-hex' : colors['hex'],
'data-var' : colors['var']
}).appendTo('#picker');
})
});
}
}
(This is not a color-picker, just a list of colors)
I want setColors() to be executed as soon as the page starts. I read that an anonymous function runs automatically, but this one isn't, I also tried...
$(function(){
setColors();
});
Below the setColors() function and that isn't working ether (The page is just blank). What am I doing wrong and how do I get my function to run on page load? I'm trying to learn so an explanation would be great.
Anonymous functions are not run immediately, you're thinking of Immediately Invoked Function Expressions which happen to often use an anonymous function.
To fix your code:
a) get rid of the this.init function wrapper within the "object" - you're not using it and this.foo only makes sense if you're using new to instantiate an object:
function setColors() {
return $.getJSON(...);
}
Note that returning the $.getJSON() result allows you to register additional deferred object handlers, register error handlers, etc.
b) call the above function in a document.ready handler (which you must do, since the AJAX callback modifies the DOM).
$(setColors);
NB: the latter is a legal way of calling this handler - jQuery will automatically register any function that you pass this way as a document.ready handler. It's similar to writing:
$(function() { setColors() })
but without the extra (useless) function wrapper.
To have that run once the DOM is initialized, you can put it in a ready listener (jQuery):
$(document).on('ready', function() {
setColors();
});
If you want the function to run automatically as soon as it is encountered in the js, after the } that ends the function, add ();
Something like:
function setColors() {
// Code
}();
setColors doesn't return the next function, or call it at the end. YOu could change it to look like:
var setColors = function(){
this.init = function(){
$.getJSON('js/colors.json', function(colors) {
$.each(colors, function(i, colors) {
$('<li>', {
text: colors['color'],
'name' : colors['color'],
'data-hex' : colors['hex'],
'data-var' : colors['var']
}).appendTo('#picker');
})
});
}
init(); // <--- change
}
Which would do the trick for you. You don't even need to "return it" either since the init function itself doesn't return anything, so you could just call it.
I’ve got a JavaScript object built like this:
var Dashboard = {
form: {
action: function(){
return $('#upload_form').attr('action');
}(),
//snip (more functions)
}
}
If I call (using Chrome 17 on WinXP) Dashboard.form.action in the Chrome console after the page loaded (I checked the script and the function is there) the result is undefined but, if I then redefine Dashboard.form.action using the same function:
Dashboard.form.action = function(){
return $('#upload_form').attr('action');
}();
and subsequently call it, it works as expected!
What am I doing wrong? Is this a bug or am I just overthinking it?
Update:
Re your comment below:
actually what I want to do IS assigning the result to the action property...
In the question you said:
If I call...Dashboard.form.action...
which makes it seem like you're expecting action to be a function (you don't "call" non-functions).
If you're expecting it to be a string (the "action" attribute value from #upload_form), then you don't need to use a function at all. But you do need to be sure that you're doing it after the #upload_form element already exists in the DOM.
To do that, either put your script below it in the markup (anywhere below it is fine; just before or just after the closing </body> tag works well), or wrap your script in a ready call.
So your code becomes either this if it's after the #upload_form in the markup:
var Dashboard = {
form : {
action : $('#upload_form').attr('action'),
//snip (more functions)
}
};
...or this if you want to use ready (anything else using Dashboard will have to wait until ready as well):
jQuery(function($) {
var Dashboard = {
form : {
action : $('#upload_form').attr('action'),
//snip (more functions)
}
};
});
Note that in the second case, Dashboard will no longer be a global variable. That's a good thing, but if you need it to be global for some reason, you can export it:
jQuery(function($) {
var Dashboard = {
form : {
action : $('#upload_form').attr('action'),
//snip (more functions)
}
};
// Export the global
window.Dashboard = Dashboard;
});
Just make sure nothing tries to use Dashboard before ready has fired.
Original answer:
You have an extra pair of () on that:
action: function(){return $('#upload_form').attr('action');}()
// here -------^^
By putting them there, you call the function immediately, and assign the result of calling it to the action property. You just want to assign the function itself to the property, so don't put the () at the end to call it:
action: function(){return $('#upload_form').attr('action');}
This is for exactly the same reason you wouldn't use () here (let's assume you have a function called foo) if you want f to refer to foo:
var f = foo;
If you said
var f = foo();
...you'd be calling foo, not referring to it.
I am relatively new to javascript so please be patient if what i am asking is completely stupid!
I am trying to make a simple module. Inside the module i want to have a config object that holds settings for the module. I am also using jquery. The jquery selectors work only when in a function directly in the main object/module.
I understand that javascript has functional scope so I am suprised that I cannot use the jquery selectors anywhere inside the module.
EDIT:
I want to be able to directly set all of my configs inside the configs object using jquery selectors. This way i keep all the messy stuff inside one place and can then access configs.whatever throughout the rest of the module. At the moment jquery selectors do not work inside the configs module.
var OB = function() {
var configs = {
'mode' : 'test',
'numOfSelects' : $('.mySelect').find('select').length, // This doesnt work
}
var getMode = function() {
return configs.mode;
}
function init() {
alert(configs.numOfSelects); // This alerts 0 until the following line
alert($('.mySelect').find('select').length); // This correctly alerts 2
};
var handlers = {
successHandler : function() {
alert("Success");
},
errorHandler : function() {
alert("error");
}
}
return {
init : init,
getMode : getMode
}
}( );
$(document).ready(function(){
OB.init();
});
It isn't that jQuery isn't in scope — that's that the code isn't executing when you think it is. The variable config is defined when that anonymous function (var OB = function() {}()) is executed. The DOM isn't ready yet, so that DOM traversal doesn't find anything. When you do the DOM traversal in init(), that isn't executed until it's explicitly called inside the $(document).ready() handler, at which point that DOM is set up. That's the difference you're seeing.
OB() needs to be called after the DOM has completely loaded. Hence the answer by Marcelo, which calls OB() in the ready() method.
EDIT: It's funny that my original answer below was incorrect because I didn't notice two little parentheses at the end of the definition of OB, and it turns out that these are the culprit. You define and then immediately invoke OB, which is before the DOM has been fully loaded. Remove those parentheses and make the change I suggest below.
Calling OB() returns an object with init and getMode, but you haven't called OB(), you've only referred to OB. Try this instead:
$(document).ready(function(){
OB().init();
});
Also, I assume you want to later refer to getMode. In particular, you will to get the copy of getMode that has access to the same local scope that your init() call had access to. To achieve this, you will need to store the result of calling OB() for later use:
var ob;
$(document).ready(function(){
ob = OB();
ob.init();
});
function some_other_function() {
... ob.getMode() ...;
}