I have defined a class named MyClass and I have defined two methods myMethod1 and myMethod2 for it:
function MyClass() {}
MyClass.prototype.myMethod1 = function() {...};
MyClass.prototype.myMethod2 = function() {...};
Inside myMethod1, I use jQuery and there's a callback closure defined there:
MyClass.prototype.myMethod2 = function() {
$.jQuery({success: function(data) {
this.myMethod2();
}, ...});
}
Now the problem is that this no longer is referring to MyClass. The question is how can I refer to it? At the moment I have assigned it to a variable named thisObj and access it this way:
MyClass.prototype.myMethod2 = function() {
var thisObj = this;
$.jQuery({success: function(data) {
thisObj.myMethod2();
}, ...});
}
Is there a better way to access MyClass.this from the closure nested in myMethod2?
Thanks in advance.
The method you've used is often called the "that reference", because the name that is commonly used as a name for the copy of the this reference. See Crockford's talks on JavaScript for example.
Your solution is perfectly fine. Since you already have a closure there, may as well make use of it, that's absolutely fine.
But if you like, you can use jQuery.proxy instead, like this:
MyClass.prototype.myMethod2 = function() {
$.jQuery({success: jQuery.proxy(function(data) {
this.myMethod2();
}, this), ...});
}
Again, though, there's nothing wrong with your original solution. Using proxy can be helpful, though, when you want to reuse a function in lots of different places, or when you don't already have a closure and don't want to introduce one (perhaps because it would close over a lot of unrelated stuff). The good thing about proxy is that it creates a closure over a controlled set of stuff and not over the current scope.
You can pass a reference to this to the function, but your solution seems fine to me. You are just using the lexical scoping rules of Javascript so what is wrong?
Related
Often in my Backbone code I come across situations where I would be passing a closure to some function and lose context of 'this'.
My solution for some time had been to do what I had seen some others do:
var self = this;
this.deferred.done(function () {
self.render();
});
Or actually I switched to _this = this, but that's beside the point. It works, but it feels ugly and I sometimes have to do it quite often. So I'm trying to figure out a better way to do this. I learned I could do this:
this.deferred.done(function () {
this.render();
}.apply(this));
And I think I could also use Underscore to do this:
this.deferred.done(_.bind(function () {
self.render();
}, this));
The apply method seems the most succinct but I feel like it has a side effect (I just don't know what it is yet).
Edit:
Take a look at this JSbin where I use apply similar to as I mentioned:
http://jsbin.com/qobumu/edit?js,console
It works, yet it throws an error at the same time. If I change the apply to bind, it works and doesn't throw an error.
Function.bind is a native method, no need for underscore unless you're coding for antique browsers. Exactly what #dandavis said: this.deferred.done(this.render.bind(this)) (but note that bind can also bind function arguments, not just this)
if you're actually coding for cutting-edge browsers or node.js 4, you can use arrow functions which bind this lexically to whatever it is in the scope where the function is defined, so you could write:
this.deferred.done(() => { this.render() });
Those do different things.
// returns a function with `this` set to what you want.
_.bind(fn, this);
// or
fn.bind(this);
// EXECUTES the function with `this` set to what you want.
fn.apply(this);
So in your case it's not a callback at all. When you use apply you are executing the function when you think you are assigning the callback.
This is why you use bind.
I am writing my first jQuery plugin, and I'm not entirely sure what should be inside the extension declaration and what shouldn't.
$.fn.myplugin = function () {
var somevar = this;
someFunc(somevar);
};
function someFunc() {/*doSomethin'*/};
OR
$.fn.myplugin = function () {
var somevar = this;
someFunc(somevar);
function someFunc() {/*doSomethin'*/};
};
I'd use the first option because:
It doesn't have any negative side effects.
That's where you are wrong. If you are working with different libraries you risk overwriting someone elses someFunc(), possibly breaking whatever they were trying to do for you. A safer way would be to wrap a closure around your code.
(function(){
$.fn.myplugin = function () {
var somevar = this;
someFunc(somevar);
};
function someFunc() {/*doSomethin'*/};
/* Do whatever other things you need someFunc/myplugin for */
})();
This way your someFunc is shielded from the global namespace.
Alternatively what you might be looking to do is to expose the method of the object to the outside world. Take following example:
$.fn.dog = function () {
this.bark = function() {alert("Woof");};
return this;
};
var $max = new $('#myDogMax').dog();
$max.bark();
This keeps the function within the context of your object but makes it possible to access it cleanly from the outside. Although this usually means that the method is somehow related to the object. It would make little sense to write a bark() function globally, as it are usually dogs that tend do it and not browser windows.
In your first method, you end up polluting the global scope adding someFunc(). The second method doesn't.
However, that doesn't automatically mean the second method is better. If you enclose the first method in a closure, it basically ends up being the exact same thing, and comes down to personal preference.
Here, it might even be better to use the first method (in a closure), so that if you have multiple jQuery extensions in a single JS file, they can share this base function.
I've stumbled over a problem. I have an object method foo defined as:
var obj = {
foo: function() {
$('.personName').mouseover(function() {
this.highlight($(this).attr('faceIndex'));
});
}
}
So what should happen is that whenever the mouse cursor is over an HTML object of type personName, the obj.highlight method should be called with the faceIndex value from the HTML object as an argument. However I apparently have a clash between two this's: the one of jQuery and the one of JavaScript (referencing to obj from inside obj).
What can (should) I do? Have I violated some good programming practice?
A typical pattern to work around this is to use a local variable to store the first this:
var obj = {
foo: function() {
var _this = this;
$('.personName').mouseover(function() {
_this.highlight($(this).attr('faceIndex'));
});
}
}
Using a language like TypeScript or an ES6 compiler makes it easier to use this pattern without having to write the _this by hand each time.
Short answer: do
$('.personName').mouseover(function(event) {
obj.highlight($(event.target).attr('faceIndex'));
});
Longer explanation:
Javascript doesn't really have a concept of this. At least not in the way you're used to thinking of it. Oh there's a keyword alright, and it kind of does the thing you expect a lot of the times but it doesn't work the way that you probably think.
The fact of the matter is that in javascipt, this is no different than any other parameter. Let me show you.
Most people are aware that in javascript you can invoke functions like this doSomething(param1, param2) or like this doSomething.call(null, param1, param2). If you wanted, you can write all function invocations using .call
See that null there? Anything you pass in there is what this gets set to.
doSomething.call(null, param1, param2);
doSomething.call(obj, param1, param2);
doSomething.call(window, param1, param2);
doSomething.call("foobar", param1, param2);
If you don't use .call the runtime just takes a guess at what value you want there.
So given this, consider that the only difference between this and any other parameter is that you don't get to give this a name! Your problem is that you have two function scopes and the inner one has a variable named this which hides the outer one's this.
Solution: don't use this. Most libraries in fact (jquery included), don't force you to use this and also pass in the value as a regular parameter
$('.personName').mouseover(function(event) {
obj.highlight($(event.target).attr('faceIndex'));
});
ambiguity solved!
Avoid using this in JavaScript, if at all possible. It is almost never necessary.
this in javascript is a very difficult thing to understand in callbacks because it may refer to virtually any instance. And that is because the callback is called from a different context.
The long story : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
As an alternative to previous answers :
One way I prefer dealing with it is by using a bind, also known as a proxy (in JQuery). jQuery has one implemented here : jQuery.proxy.
It has the benefit of letting you chose who is your this in the callback function.
For example:
var obj = {
foo: function() {
$('.personName').mouseover($.proxy(function(event) {
// this refers here to obj instance
console.log(this);
// event is a jQuery decorated that holds the reference of your element
console.log(event);
}, this));
}
};
And the true benefit of it, is that it lets you construct components that don't have "ugly" nested callback anonymous functions:
var obj = {
foo: function() {
$('.personName').mouseover($.proxy(this.mouseOverCallback, this));
},
mouseOverCallback : function(event) {
// this refers here to obj instance
console.log(this);
// event is a jQuery decorated that holds the reference of your element
console.log(event);
}
};
Lately I've been trying to make an object in JavaScript with the following structure:
function colorDiv(div){
this.div=div;
this.div.bind("click",this.changeColor)
this.changeColor(){
this.div.css("background", "#FF0000");
}
}
The problem is that the changeColor method cannot be called from the jQuery environment because this must refer to the current colorDiv object, so the bind method cannot work as expected.
How can this be solved?
There are a couple ways. The simplest is as follows:
function ColorDiv(div) {
var that = this;
that.div = div;
that.div.bind("click", that.changeColor);
that.changeColor = function () {
that.div.css("background", "#FF0000");
};
}
var colorDiv = new ColorDiv($("#my-div"));
$("#something-else").click(colorDiv.changeColor);
You save a reference to this in the variable that, which is just the name commonly used in the JavaScript world for exactly this purpose. Then you refer to that instead of this inside your changeColor method. (Note that I used that everywhere, just for consistency, even though the only place it actually makes a difference is inside the changeColor method.)
Another is to use the Function#bind method. You can either use it every time you call changeColor, like so:
var colorDiv = new ColorDiv($("#my-div"));
$("#something-else").click(colorDiv.changeColor.bind(colorDiv));
or you can use it in the ColorDiv class to ensure that all methods are bound correctly whenever they are called:
this.changeColor = (function () {
this.div.css("background", "#FF0000");
}).bind(this);
As noted in the linked article, Function#bind is not supported in all browsers, so you'll need a shim like the one they give, or possibly a full-fledged ES5 shim.
Underscore.js has a bindAll function that could be useful here, too, especially with multiple methods:
_.bindAll(this);
Finally, it's worth noting you don't really need to do any of this in your particular example: just do
this.changeColor = function () {
div.css("background", "#FF0000");
};
instead of what you have, i.e. reference the div variable passed in, instead of the reference stored in this.div, since they are the same thing.
Try setting as the first line of the method:
var self = this;
and then using self as needed.
this.div.bind("click",self.changeColor)
there is a way to know what class own a function?
Example:
function globalFunc(){
//alert MyObject
}
function MyObject(){
}
MyObject.prototype.test=function(){
globalFunc();
}
var o=new MyObject();
o.test(); //alert MyObject
Now im using this workaround:
function globalFunc(){
alert(globalFunc.caller.__class__);
}
function MyObject(){
}
MyObject.prototype.test=function(){
globalFunc();
}
MyObject.prototype.test.__class__=MyObject;
var o=new MyObject();
o.test(); //alert MyObject
But there is a big problem, look this:
function globalFunc(){
alert(globalFunc.caller.__class__);
}
function MyObject(){
}
MyObject.prototype.test=function(){
var temp=function(){
globalFunc();
}
temp();
/* to simulate a simple closure, this may be for example:
element.addEventListener("click",temp,false);
*/
}
MyObject.prototype.test.__class__=MyObject;
var o=new MyObject();
o.test(); //alert undefined
So, there is a clear way to obtain this?
I know where is the problem(class property is a property of only test and not temp), but i can't add class to temp too.
Thanks.
Thanks for reply, some clarification.
Im trying to do a personal framwork OO oriented with private members.
So:
globalFunc is a special function, im using it to get "private" property and i can't call it with call method or passing some arguments, the only arguments im pass is "this":
Example, $() is global
Class({
public:{
MyClass:function(){
},
setName:function(name) {
$(this).name=name; //set the private var name
},
getName:function(){
return $(this).name;
}
},
private:{
name:"UNKNOWN"
}
})
var o=new MyClass();
o.getName(); // UNKNOWN
o.setName("TEST!!!");
o.getName(); // TEST!!!
o.name; //undefined
$(o).name; //undefined
To works with inheritance, $(), i need to know what class call it and the object of the class.
All works good, but if i need to access a private members in a clousure i must add
__class__
property to clouser!! And i not want this!
Thanks again and sorry for my bad english, im not native speaker.
In javascript there are no Classes. Instead several objects can "own" the same function. For example:
function myFun(){
alert(this.name);
}
function Obj1(){
this.name = "obj1";
}
Obj1.prototype.fun = myFun;
function Obj2(){
this.name = "obj2";
}
Obj2.prototype.fun = myFun;
var obj1 = new Obj1();
var obj2 = new Obj2();
obj1.fun();
obj2.fun();
You scenario is not entirely clear but here are some options:-
function globalFunc()
{
alert(this.__class__);
//Note globalFunc now has access to much more.
}
function MyObject(){ }
MyObject.prototype.test=function(){
globalFunc.call(this);
}
MyObject.prototype.__class__=MyObject;
To add a closure for event handling
MyObject.prototype.test = function(){
var self = this;
var elem = //get some element;
//Not cross-browser but for illustration
elem.addEventListener('click', fnEvent);
function fnEvent() { globalFunc.call(self); }
elem = null
}
I don't understand well what you are trying to do, but here's an idea that could inspire you something.
The "constructor" property helped me well when I was trying to use JS as a OO language.
o.constructor will give you the myObject function reference.
But in my opinion, you should give functional programming a try instead of OO to get the most from Javascript
temp is the caller but it does not have the property __class__. You only defined that for test.
caller is non-standardised and really brittle; best avoided. Much better to pass the value explicitly:
MyObject.prototype.test= function() {
element.addEventListener('click', function() {
globalFunc(MyObject);
});
};
or:
MyObject.prototype.test= function() {
var myclass= arguments.callee.__class__;
element.addEventListener('click', function() {
globalFunc(myclass);
});
};
Functions are not "owned" in JavaScript. Functions are first class objects in JavaScript, which means they can be passed around like any other variable and assigned as properties of many objects.
this inside a function body will give you a reference to the object on which the function was called, if called as a method (e.g. myObj.myMethod()) or via the Function prototype's call() or apply() methods (e.g. myFunc.call(myObj)). Inside a function called on its own (e.g. myFunc()), this will usually be the global object (the same as window in most browsers).
Short answer, there really is no way to do what you are trying to do. Javascript just doesn't work like that.
Long answer...
Almost every programmer I've met who has come to JavaScript from a language that uses a classical object model has tried to do what you are doing: emulate the classical OOP style they are used to with Javascript.
There is no shame here, I did this, even the famous Douglas Crockford experimented with this, and then later gave it up. I did too. I still think it was a neccessary thing for me to try this stuff in order to really learn the langauage
Do not be fooled by the curly braces, and familiar looking operators. At it's core it has very little in common with Java! JavaScript is a functional langauge: functions are objects in their own right.
Bluntly: There is no way to do what you are trying to do - have objects with private properties you can access through this
Saying that is easy :) Understanding how to apply that is more difficult
There are no classes, there are only objects.
Objects only have properties.
Properties of objects may have values.
Those values may be functions.
this is set when the function gets called, it could be anything
Closure is the only way to achieve true privacy in JavaScript. Everything else either leaks into some enclosing scope, relies on obfuscated property names, or can be circumvented in some way by the caller, or leaks memory even after your objects are no longer referenced (as there are no destructor function to do your clean-up in).
What you are doing is still a good thing to try and do, by attempting it you will become a better JavaScript programmer by figuring out:
why you can't do some of it
more importantly why you probably shouldn't