Best way to define and call functions in Javascript - javascript

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.

Related

Variable scope within function

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.

JavaScript 'this' in different context

I am trying to create a JavaScript object as follows.
var MyObject = function (parameters) {
this.parameters = parameters;
parameters.userFunction(this.MyObjectCallback);
}
MyObject.SOME_STATIC_VARIABLE = 21;
MyObject.prototype = {
myObjectCallback: function() {
console.log(this);
}
}
The MyObject object will accept a userFunction to which it will pass a handler. The user function will do some logic and pass the result back to the instance, for example:
new MyObject({userFunction: function(callback) {
$.post(
'http://localhost/~knyttl/source.php',
{},
callback,
'json');,
}});
Unfortunately, even though the callback is properly called, this gets an instance of the JQuery object and not of the MyObject instance as I would like. To conclude, I can not manage to keep the MyObject instance.
I am not even sure, whether this is a correct way of creating JavaScript objects. I will be grateful for any suggestion.
You can bind a specific this value using .bind. Also I corrected the capitalizing of My.
parameters.userFunction(this.myObjectCallback.bind(this));
When you call a function like a.b(), then inside b, this === a. However, if you do not directly call it but only pass the function (like a.b) and call it later, this binding is lost.
.bind returns a new function which now receives the jQuery ajax result as this. However, it ignores that and calls myObjectCallback with the predefined (bound) this.
.bind is not available on older browsers but there are shims available.
See apply() and call().
parameters.userFunction.call(this, this.MyObjectCallback);
jQuery allows you to set the context of your callback.
You are in a weird situation where you design has hurt you. Your MyObject can't be passed in as the context, because it is being created at the same time.
new MyObject({userFunction: function(callback) {
$.post(
'http://localhost/~knyttl/source.php',
{},
callback,
'json');,
}});
So instead:
var myObj = new MyObejct();
myObj.setCallback({userFunction: function (callback) {
$.ajax({
context: myObj,
url: 'http://localhost/what ever /',
success: callback,
dataType: 'json',
data: {}
}
});
Try this.
var myObject = {
obj1:function(paremeters){
this.Name = paremeters
return this},
};
I would suggest you read,
http://javascript.crockford.com/javascript.html
Using this in javascript has the potential to be very confusing. It is assigned the value of whatever is behind the dot when the function is called e.g.
window.doSomething();
will cause this to be set to window, whereas
myDOMElement.onClick();
will cause this to be set to myDOMElement.
In your case, it comes down to JQuery's internal workings, so have a look at this very thorough set of explanations to get an understanding of how JQuery handles this.

How to assign use this in a construct

Im a bit of a beginner when it comes to javascript constructs. Im trying in jquery, but not with much success. The following is a reduces version of my code:
var field1 = {
fieldId:"#field1",
field:"",
init:function(){
this.field = this;
$.ajax({
type:"GET",
url:'some/url/to/get/values/from',
cache:false,
success: function(data){
alert(field.fieldId);
}
});
}
};
field1.init();
Basically i want to be able to print out field.fieldid inside the success event but i end up with something most definetly not expected. I would hate having to write field1.field.fieldid everytime also since that would ruin when i figure out how to use extends and similar things.
Can anyone help me get "#field1" out when i do the alert(field.fieldId) ?
This is a classic case of You must remember this. The simplest answer in your case is a local variable in your init function:
var field1 = {
fieldId:"#field1",
field:"",
init:function(){
var self = this; // <=== change here
$.ajax({
type:"GET",
url:'some/url/to/get/values/from',
cache:false,
success: function(data){
alert(self.fieldId); // <=== and here
}
});
}
};
field1.init();
Or alternately, use the context argument of the ajax function:
var field1 = {
fieldId:"#field1",
field:"",
init:function(){
// <=== change here (no `this.field = this;`)
$.ajax({
type:"GET",
url:'some/url/to/get/values/from',
cache:false,
context: this, // <=== and here
success: function(data){
alert(this.fieldId); // <=== and here
}
});
}
};
field1.init();
Basically, in JavaScript, the value of this during a function call is defined entirely by how a function is called, not where it's defined as in some other languages (C++, Java, C#, ...). When jQuery calls the success callback of the ajax function, it has to set this to something. By default, it sets it to an object representing the settings of the ajax call, but using context you can tell jQuery to set it to something else, allowing you to use this within the callback to mean the same thing as this when you call ajax.
The first solution takes advantage of the fact that the success callback is a closure over the context of the call to init (don't worry, closures are not complicated), and so by creating a variable (self) and giving it the value of this, we can reliably refer to the object via self within the closure (the success callback).
Ways in which this is set in JavaScript:
When you call a function by getting the function reference from an object property as part of the same expression as the call, this within the function call will be the object from which you got the property. So given:
var obj = {
firstName: "Fred",
speak: function(msg) {
alert(this.firstName + " says " + msg);
}
};
then
obj.speak("hi"); // alerts "Fred says hi", because `this = obj` within the call
Note that it has to be part of the same expression as the call. This does not work:
var s = obj.speak; // Getting a reference to `obj`'s `speak`
s("hi"); // alerts "undefined says hi", because `this` is not
// `obj` during the call
Using call or apply. These are features of all JavaScript functions. They let you call the function and explicitly set what this will be during the call. So given the obj above:
var s = obj.speak; // Getting a reference to `obj`'s `speak`
s.call(obj, "hi"); // alerts "Fred says hi", we explicitly made `this = obj`
// within the call
The only difference between call and apply is that when you use call, if you want to pass arguments to the function, you include them as discrete arguments to call as above (note we just passed "hi" as a second argument to call, and call passed it on as the first argument to the function). With apply, rather than an unlimited number of discrete arguments, the second argument is an array of arguments to pass to the function.
// Example 1: Passing no arguments, no difference.
// These calls do the same thing.
func.call(obj);
func.apply(obj);
// Example 2: Passing one argument (these calls do the same thing).
func.call(obj, arg);
func.apply(obj, [arg]); // note that it's in an array
// Example 3: Passing two arguments (these calls do the same thing).
func.call(obj, arg1, arg2);
func.apply(obj, [arg1, arg2]); // Again, the args are in an array

How to correctly use Javascript 'this' in my case?

I have a following (simplified) code:
var myModule = {
submitDummyForm: function(){
console.log(this); // the right object is logged out
var that = this; // keep a reference
$.ajax({
type:'POST',
url: 'http://localhost/',
data: {dummyData: 'something'},
dataType: 'json',
success: that.dummyFormSuccess
});
},
dummyFormSuccess: function(data){
console.log(this); // 'this' is logged out as some foreign object, most probably jQuery.ajax object
}
}
It leads to 'this' being lost in the dummyFormSuccess, no matter if I use this.dummyFormSuccess or that.dummyFormSuccessfor as an argument for my ajaxSubmitForm().
But the following code gets executed as I need:
var myModule = {
submitDummyForm: function(){
console.log(this); // the right object is logged out
var that = this; // keep a reference
$.ajax({
type:'POST',
url: 'http://localhost/',
data: {dummyData: 'something'},
dataType: 'json',
success: function(data) {
that.dummyFormSuccess(data);
}
});
},
dummyFormSuccess: function(data){
console.log(this); // now 'this' is logged out correctly as the real myModule object
}
}
I'm still not very comfortable with advanced topics of Javascript but I already know, that 'this' may get redefined, depending on where it is used. I thought if I use 'that' to store the reference to 'this', it should also keep my 'this' inside the called function. It seems weird, that I can call that.dummyFormSuccess(data) in a wrap-around function and it gets correct 'this' inside, but if I just assign it to $.ajax success, my 'this' gets lost.
Can anybody explain, where is 'this' getting lost in my case and why it works OK in the second example? Is it a problem with jQuery (maybe jQuery.ajax() overwrites my 'this' somehow in my case) or just a feature of the language?
Everything is correct. Your this is lost in a first example because you are assigning function that.dummyFormSuccess to jQuery ajax object's success. So, this way, deep inside jQuery, it's called something like ajax.success. So, this is overwritten with ajax object.
With second approach you create an anonymous function and assgn it to success. So inside your anonymous function, this points to ajax object, but that variable is accessible and have not been overwritten.
You're misunderstanding this.
The value of this parameter is determined by the callsite – the code that calls your function.
When you pass that.dummyFormSuccess or this.dummyFormSuccess, you're just passing a function that happens to come from your object.
The this or that object is just used to retrieve the function instance; it isn't bundled with the function.
When jQuery calls your callback, it always calls it in the context of the jqXHR object.
When you pass function(data) { that.dummyFormSuccess(data); }, your function expression is called in the context of this jqXHR object.
However, your callback then calls dummyFormSuccess in the context of that, so its this is what you want it to be.
You can treat 'this' in JavaScript as additional argument in function argument list.
So each call of function will contain its own value in this argument. Here are all three ways of invoking function in JS
foo(); - inside the function this will be set to default
namespace object (window in case of browser environment).
obj.foo(); - this will get obj reference inside the foo().
foo.call(obj); - 'foo' will be called with this set to obj (same as above)
Since a function can also mean an object (and it is an object), this means that function. (different scopes)
What you need to do is put var that = this; outside of dummyForm : function (the line above it), and then do console.log(that).
var that = this;
dummyFormSuccess: function(data){
console.log(that); // now 'this' is logged out correctly as the real myModule object
}

AJAX load in conjunction with Getters and Setters

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.

Categories