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.
Related
I have a function, functionWithDifferentScope, that takes an object, myobject.options, as a parameter. One of the pairs in the options object is a callback which points to a function defined in myObject: myCallback.
What I'm trying to achieve is injection of the myObject namespace into the callback of a function that is defined (by a 3rd party) at the global level.
A simplified example:
var myObject = {
options: {
callback: this.myCallback(this),
...,
},
init: function() {
// functionWithDifferentScope operates in the 'window' context
functionWithDifferentScope(this.options);
},
myCallback: function(namespace) {
// 'this' is window
// 'namespace' is myObject
}
}
myObject.init();
When executing this script, this.myCallback(this) appears to be executed at definition (due to the parenthesis?); as well as once myObject.init(); is caled. During the first executions this is myObject, but subsequent calls through the functionWithDifferentScope identify this as window.
Is there a way to pass the myObject namespace to the myObject.options.callback value as a parameter?
Do you mean this?
var myObject = new (function() {
var t = this;
vac callback = function() {
// t equals to the myObject-instance
// this equals to window
}
this.init = function() {
funcWithDifferencScope(callback);
}
})();
myObject.init();
I think what you are looking for is prototype style "bind"
Basically "this.myCallback(this)" is a call to the function.
this.myCallback is the function itself. (It is an object with the type function).
You can call it using the method 'call' or 'apply' that you can use on functions. Which will call these functions.
See:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fapply
The first argument is the object context to work in. What I think you mean by object namespace.
so: a.callback(5) is the same as a.callback.call(a,5)
However please note that these days if you are working with most javascript libraries you probably have a 'bind' function that will do the work for you.
http://prototypejs.org/doc/latest/language/Function/prototype/bind/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
the idea is that this.callback.bind(this) returns a Function object you can call that will inject the correct context automatically so you can pass the return value of bind alone as a callback and be assured that the method will be executed on the correct object.
I'm attempting to have a javascript object run a deferred method and when it's .done() call a function in that same object. I'm having issues because "this" becomes the deferred object instead of the object that called it.
PageObject.prototype.successFunction = function() {
console.log(arguments);
return console.log(this.name + " Success function called");
};
PageObject.prototype.loadPage = function(url) {
return $.when($.mobile.loadPage("pages/" + url))
.done(this.successFunction);
};
var pg = new PageObject();
pg.loadPage("test.html");
How do I send "this" into the successFunction? This PageObject is going to be extended by others as well, so knowing "this" when running successFunction will be very handy.
It seems simple, and probably has a simple answer. I was looking into .apply() but I'm not sure it helped. This post on stack overflow was helpful a little bit, but it broke the minute I put it into the .done() function.
Functions as parameters (with parameters) -- JavaScript
jQuery's proxy will return a function bound to a specific context:
PageObject.prototype.loadPage = function(url) {
return $.when($.mobile.loadPage("pages/" + url))
.done($.proxy(this.successFunction, this));
};
There is also ES5's bind which operates similarly but needs to be shimmed in older browsers
PageObject.prototype.loadPage = function(url) {
return $.when($.mobile.loadPage("pages/" + url))
.done(this.successFunction.bind(this));
};
apply and call can execute a function immediately with the specified context, but proxy and bind return a function that can be used later or passed to other functions.
The this "variable" in JavaScript is called the "calling object" and is determined when its enclosing function is called (not when it's defined). It can be set a few different ways:
You can set it by using the . operator (e.g. myObject.myFunction())
You can set it by using a function's call() or apply() methods
You can (under ES5) bind it permanently by using the function's bind() method
It will default to the global object if none of the above methods set it to something else
The important thing to understand is that (aside from method 3 above), it's all about how the function is called when it's called, not how it's referenced, not how it's stored, not where it's created.
In your case, you're passing this.successFunction as a function reference to be called by jQuery, but it doesn't call it that way (because it just got a reference to the function, not any information on how that function should be called).
There are a few fancy tricks you can use with jQuery's $.proxy() or ES5's .bind() methods, but when it comes down to it, the most straightforward way to handle it is to simply retain this through a closure-scoped variable and use a function wrapper:
PageObject.prototype.loadPage = function(url) {
var self = this;
return $.when($.mobile.loadPage("pages/" + url))
.done(function () { self.successFunction(); });
};
Note that we're using method 1 to explicitly bind successFunction's calling object to be the same as loadPage's calling object. It's short, simple, clear, works fine under ES3, and doesn't depend on jQuery.
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 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
}
Consider this:
window.onload = function () {
myObj.init();
};
var myObj = {
init: function () {
console.log("init: Let's call the callMe method...");
//callMe is not defined...
callMe();
//Works fine!
this.callMe();
},
callMe: function () {
console.log('callMe');
}
};
Since the init function gets called this way (myObj.init), I expect this to be myObj in the init function. And if that is the case, why the callMe function fails? How am I supposed to call the callMe function without using the this context in the init body? (Actually, it's too annoying to call the object methods using this over and over again through the functions. So what's the point of having a single object?)
I would like to know how can I fix this so that the callMe method gets called using the first invocation in the code above?
this is never implicit in JavaScript as it is in some other languages. Although there are ways to do it, like this using the with statement:
init: function () {
console.log("init: Let's call the callMe method...");
// Make `this` implicit (SEE BELOW, not recommended)
with (this) {
// Works
callMe();
}
},
...it's generally a bad idea. Douglas Crockford probably wrote one of the better descriptions of why it's a bad idea, which you can find here. Basically, using with makes it nearly impossible to tell what the code's going to do (and slows the code down, if you do anything else in that with statement that doesn't come from the this object).
This isn't the only way that JavaScript's this is not the same as it is in some other languages. In JavaScript, this is defined entirely by how a function is called, not where the function is defined. When you do this.callMe() (or the equivalent this["callMe"](), or of course foo.callMe(), etc.), two things happen: The function reference is retrieved from the property, and the function is called in a special way to set this to be the object that property came from. If you don't call a function through a property that way, the call doesn't set any particular this value and you get the default (which is the global object; window on browsers). It's the act of making the call that sets what this is. I've explored this in depth in a couple of articles on my blog, here and here.
This (no pun) can be made even clearer if you look at JavaScript's call and apply functions, which are available on all function objects. If I do this:
callMe.call({});
...it'll call the callMe function with a blank object ({}) as this.
So basically, just get used to typing this. :-) It's still useful to have properties and methods associated with an object, even without the syntactic convenience (and confusion!) of an implicit this.
You can also use the module pattern, which captures all private variables inside a closure, so you are free to use them without this, as they're in the same scope. You then pick and choose which methods/variables you want to make public:
var myObj = (function () {
var init = function () {
callMe(); // This now works
};
var callMe = function () {
...
};
// Now choose your public methods (they can even be renamed):
return {
init: init, // Same name
callMyName: callMe // Different name
};
}) ();
Now:
myObj.init(); // Works
myObj.callMyName(); // Works
myObj.callMe(); // Error