I've been working with getters and setters to avoid the prospect of using global variables. However, I've run into a problem. The below code, which works fine with integer variables, throws an exception when I try to run an AJAX call instead. Can someone explain to me why this is happening?
function Object_XML() {
me = this;
me.xml = null;
}
Object_XML.prototype = {
getXML: function() {
return me.xml
},
setXML: function(data) {
me.xml = data;
},
loadXML: function() {
$.ajax({
type: "GET",
url: "questions.xml",
dataType: "xml",
success: function(xml) {
me.setXML(xml);
} //close success
});//close AJAX
}//close setXML
};
$(document).ready(function() {
var data = new Object_XML();
alert("This is an " + data.getXML());
data.setXML();
alert("This is an " + data.getXML());
});
Thanks, Elliot Bonneville
You just negated your use of private variables with getters and setters by using me = this; You just made me a global variable by not using var. (any variable not defined using var gets attached to the global namespace)
In your case since you're working within the same object scope you can just use this and avoid the me as personally, i think it's confusing. But if you want to stick to that paradigm, use var me = this;
Your example is really unclear, where does the error happen? You're calling data.setXml() with no parameters, so me.xml will bet set to undefined. That is to be expected if you pass nothing into the method.
Also keep in mind that due to the async nature of your call, if you were to do something like:
data.loadXml();
console.log("data.getXML();", data.getXML()); // would be undefined
data.getXML() at that moment would still be undefined as it's likely your asynchronous call hasn't returned yet, thus not setting the xml attribute of your object.
Related
Read lots of posts lately, trying to demystify variable scopes in Javascript/SAPUI5. The way that I have understood things we have mainly 2 kinds of them - local variables, declared inside functions and visible only within and global variables declared outside functions and therefore "globally" accessible. Tried that with a really small example and, after what debugging revealed, I feel quite puzzled here. Tiny sample below:
onTestButtonPress: function() {
var url = "/DEV/sap/opu/odata/SAP/ZCONTRACTS_SRV/Agreement_ExportSet";
var oTest1 = [];
var promise = $.Deferred();
$.ajax({
url: url,
type: "GET",
dataType: "json",
success: function(xhrData) {
var oTest2 = xhrData;
promise.resolve();
}
});
var readyToGo = function() {
jQuery.sap.log.error("check");
};
jQuery.when(promise).done().then( jQuery.proxy(readyToGo, this) );
}
On $.ajax... and jQuery.when... statements, oTest1 var has a value of [] while within the functions contained in the main function (ajax success function & readyToGo), same variable is producing a reference error.
According to the "theory" and unless I miss something, since oTest1 was declared at the beginning of the main function, it should be visible within the two nested functions as well. What do I miss here or, to put it in a different way, where (or how maybe?) should I declare my variable in the function in order to be visible?
Thanks in advance,
Greg
UPDATE: After a (fair) suggestion, modified the code and attached code for the variable in doubt. New code is this:
onTestButtonPress: function() {
var url = "/DEV/sap/opu/odata/SAP/ZCONTRACTS_SRV/Agreement_ExportSet";
var oTest1 = [];
var promise = $.Deferred();
$.ajax({
url: url,
type: "GET",
dataType: "json",
success: function(xhrData) {
oTest1 = xhrData;
promise.resolve();
}
});
var readyToGo = function() {
jQuery.sap.log.error(oTest1);
};
jQuery.when(promise).done().then( jQuery.proxy(readyToGo, this) );
}
This code is running just fine and variable is OK. Still running the old code, debugger produces this:
This is the reason that I didn't use the var in the sample, is this normal?
You get an error in the code in the screenshot because oTest1 is not in scope.
Scope is determined when the function is created, and since the oTest1 variable is not used in the function then it isn't in scope.
You attempt to add it to the scope after the function has been created by reading the variable with the debugger. It is too late.
Add console.log(oTest1) before debugger and it will be in scope.
Is there any trick to know when the new value is assigned to global variable in callback function in JavaScript.
As I read in here,it is asynchronous , so JavaScript does not wait until the response is received.
I need the new value in my Java code which run JavaScript code in my html page.
There is no trick because you can't write code the way you're trying to write it. You do not want to put the data into a global variable and then try to magically discover when the data is there. That's just not a good way to write async code.
Instead, you need to put the code that uses the result of the async call in the actual callback itself or call some function from the callback and pass it the data.
You don't show your actual code, but here's an example using jQuery Ajax (just for example purposes, you can do this with any code or library):
$.ajax({
type: "POST",
url: "some.php",
data: { name: "John", location: "Boston" },
success: function(data) {
// the results of the ajax call are here in the data argument
// to this callback function
// you can either use the data here (write any code here that needs to use it)
// or you can call some other function and pass it the data
}
});
No. There is no direct possibility.
One possible solution is implementing an observer-pattern or using something like backbone, where models trigger events for changes of state.
Another possible solution is, working with futures or promises. Q gives a possible approach.
The code that needs to access that variable has to be within the callback; else the variable would not have been assigned the value yet.
So you need to trigger the Java code from within the callback. Tough to provide an answer on how to do that without more information.
var callbackFunction = function(arg, callback) {
if ('an error') {
callback(new Error('error text'));
}
else {
// set the global variable
callback(null, 'result');
}
};
var callingFunction = function() {
callbackFunction('arg', function(err, result) {
if (!err) {
// Global variable is set
// Trigger call to Java
}
});
};
You can define yor global variable as object instanse:
var globalVar = {
_val: null,
changed: function () {
alert(1);
},
setValue: function(val) {
_val = val; this.changed();
},
getValue: function() {
return _val;
}
};
Use setValue and getValue to set and get a value of ur global variable:
globalVar.setValue(45); // will set the value and throw an alert box.
globalVar.getValue(); // will return 45
So u can just init 'changed' prop with ur own function and it will be fired if 'setValue' called
I have a var in jquery after a request like
type: 'POST',
data: data,
cache: false,
success: function (data) {
var json = jQuery.parseJSON(data);
I'm trying to use timeout to fire the function below after five seconds.
$("#a" + json.id).fadeOut(300);
At the moment I'm doing
setTimeout('$("#a" + json.id).fadeOut(300)', 500);
but it doesn't seem to work
setTimeout takes a function and a number as a parameter, try this:
setTimeout(function() {
$("#a" + json.id).fadeOut(300);
}, 500);
Not sure if the value of json.id changes by the time the timeout callback is called.
Consider the following example:
for (var i=0;i<10;i++){
setTimeout(function(){console.log(i)},500);
}
The callback function sees i=10 because that's the value of i by the time the function is invoked. You can do a pre-binding by using closure:
var f=function(id){
setTimeout(function(){console.log(id);},500);
}
for (var i=0;i<10;i++) f(i);
Now that you see how closure and pre-binding work, here's a more robust solution to your question:
var f=function(id){
setTimeout(function(){$('#a'+id).fadeOut(300);},500);
}
f(json.id);
Your code doesn't work because the String is eval'ed in global context. So for it to work you can make json global (remove the var).
Also, while I am not sure where you are calling the setTimeout from, but assuming it is inside the callback, you can alternatively make the id a part of the string :
setTimeout('$("#a'+json.id+'").fadeOut(300)', 500);
But certainly a better option is to avoid eval and globals at all costs (checkout Eval is evil and Why global state is the devil for an elaborate explaination) and pass in a closure :
setTimeout(function(){ $("#a" + json.id).fadeOut(300); }, 500);
Below is just some sample Javascript that I posted that shows 2 different ways that javascript functions are being defined and called.
Is there a name for these different methods?
Which method is preferred?
The first code block looks really simple, pretty much the same as a procedural PHP function is defined and called.
The second I realize is set up more like a class/namespace it just get's a little confusing for me as I have not studied javascript too much yet. Am I correct in my thinking that all these functions could be coded in either method as the first or second code blocks and still work?
Sorry if my question is not clear enough, I will revise if needed, thanks for the help/info
initCommentsHelp();
function initCommentsHelp() {
$('#view-comments-help-a').live('click', function() {
$('#comments-help').slideToggle("normal");
return false;
});
}
VS doing this
Screenshot.Like.Shot.toggle();
Screenshot.Comment.toggle();
Screenshot.Flag.flag();
Screenshot.Flag.unflag();
var Screenshot = {
Like: {
Shot: {
toggle: function() {
if ($('.fav a.fav-toggle.processing').length == 0) {
$.ajax({
type: 'POST',
url: url,
data: data,
beforeSend: function() {
$('.fav-toggle').addClass('processing');
$link.text('Wait...');
},
success: function(responseHtml) {
$('#like-section').replaceWith(responseHtml);
}
});
}
return false;
}
},
Comment: {
toggle: function() {
var link = $(this);
var data = link.hasClass('liked-by-current-user') ? {_method: 'delete'} : null;
$.ajax({
type: 'POST',
url: this.href,
data: data,
success: function(responseHtml) {
link.closest('.comment').replaceWith(responseHtml);
}
});
return false;
}
}
},
Flag: {
// Flag a screenshot as inappropriate or Appropriate
flag: function(){
var link = $(this);
var screenshotId = link.modelId();
if(!confirm("Are you sure you want to flag this shot?"))
return false;
$.ajax({
type: 'POST',
url: this.href,
data: {
screenshot_id: screenshotId
},
success: function(responseHtml) {
$('#flag-section').html(responseHtml);
}
});
return false;
},
unflag: function() {
var link = $(this);
var screenshotId = link.modelId();
$.ajax({
type: 'POST',
url: this.href,
data: {
_method: 'delete',
screenshot_id: screenshotId
},
success: function(responseHtml) {
$('#flag-section').html(responseHtml);
}
});
return false;
}
},
};
The first way is generally preferred for writing standalone functions. You can write them as
function testFunction() {
// your code here...
}
or
var testFunction = function() {
// your code here...
}
The second example you have posted is used for namespacing your objects. You can read more about namespacing in this article : Namespacing in JavaScript
A function that's an object property (called via an object reference, e.g. obj.func()) is what's called a "method". A function not associated with an object is called a "free function". Methods have special access privileges not afforded to free functions. Exactly what those privileges are depends on the language, but all OO languages include a special variable (you can consider it a hidden parameter) available within the function body to access the object the method is bound to. In JS, the name of this parameter is this.
this exists in free functions, where it refers to the global object. You can think of free functions and global variables as being properties of a global, default object. For browsers, the global object is window. Free functions, then, are similar to global variables, which are generally bad. Free functions don't as often cause problems as global variables, but they still can, and for the same reasons. As a result, some developers use objects as namespaces to prevent name collisions.
For example, one module might create a sign function that returns whether a number is positive, negative or 0. Another module might have a sign function that digitally signs a message. These modules are created by different companies, each unaware of the other. Imagine a developer wants to use both modules. If both were defined as free functions, whichever were defined second would replace the first, wreaking havoc in the other module. To prevent this, each function can be defined as properties of separate objects.
The actual difference between free functions and methods is in how they are accessed. Methods are accessed as a property of an object, while free functions are accessed directly by name or as a variable. Note that the same function can be treated as a method or free function, depending on how you access it.
var obj = {
type: 'method',
meth: function (){
return this.type;
}
};
var func = obj.meth,
name = 'free';
// the following two lines call the same function, though `this` will be bound differently for each.
obj.meth(); // returns 'method'
func(); // returns 'free'
You can even take a free function and call it as a method in a number of ways:
function func(a, b) {
return this.foo+a+b;
}
var obj = {foo: 'bar'};
// call func as a method using the `call` method
func.call(obj, 'baz', 'bam');
// call func as a method using the `apply` method
func.apply(obj, ['baz', 'bam']);
// call func as a method in the usual way
obj.meth = func;
obj.meth(1, 2); // 'foo12'
If you look more closely at your second sample, you'll note that most of the methods use the this variable. These must remain methods; making them free functions will likely cause bugs.
One is defining the functions in an object and the other is just defining a function by itself. Functions are first class objects in JavaScript so they don't need an object to be defined.
I'm trying to use a closure (I think that's what it is..), I'd just like to execute a function with a local variable at some point in the future, like this:
function boo() {
var message = 'hello!';
var grok = function() { alert(message); }
foo(grok);
}
function foo(myClosure) {
$.ajax({
timeout: 8000,
success: function(json) {
myClosure();
}
}
}
I could get around this by using global variables and such, but would rather use something like the above because it at least seems a bit cleaner. How (if possible) do you do this?
Thanks
----------- Update --------------------
Sorry I wasn't clear - I was wondering if this is the correct syntax for the closure, I tried it out and it seems ok. Thank you.
Your existing code looks perfectly fine except for that missing paren at the end. ;)
If you're looking to understand the concept of closures more deeply, think of it this way: whenever something in a closured language is defined, it maintains a reference to the local scope in which it was defined.
In the case of your code, the parameter to $.ajax() is a newly-created object ("{ timeout: 8000, etc. }"), which contains a newly-created function (the anonymous "success" function), which contains a reference to a local variable ("myClosure") in the same scope. When the "success" function finally runs, it will use that reference to the local scope to get at "myClosure", even if "foo()" ran a long time ago. The downside to this is that you can end up with a lot of unfreeable data tied up in closures -- the data won't be freed until all references to it have been removed.
In retrospect, I may have confused you more than helped you. Sorry if that's the case. :\
Unless you actually want to make an AJAX call, setTimeout might be more along the lines of what you are looking for:
function foo(myClosure) {
setTimeout(myClosure, 8000); // execute the supplied function after 8 seconds
}
If your question was more along the lines of "Am I creating a closure correctly?", then yes, your function boo is doing the right thing.
Is it what you want?
var boo = (function() {
var message = 'hello!';
return function() {
foo(function() {
alert(message);
});
};
})();
function foo(myClosure) {
$.ajax({
timeout: 8000,
success: function(json) {
myClosure();
}
}
}
or just
function boo() {
$.ajax({
timeout: 8000,
success: function(json) {
alert('hello!');
// do sth with json
// ...
}
}); // <- missed a paren
}
The example is too simple to know what you want btw.