I have a makeshift events system in JS and the attacher looks like this:
events.attach(events.NEW_TASK,this.update);
Where this.update is a callback/handler/function. The problem is, when the dispatcher tries to call this handler, it loses it's original context. How can I bind the handler with the context of the object that calls the attach function without passing this as an argument and using that?
Edit: I should also mention that it will be somewhat of a framework so it has to be simple for other developers so preferably not needing to store the context in another variable.
Another option, using the Function.prototype.bind method, part of the new ECMAScript Standard:
events.attach(events.NEW_TASK, this.update.bind(this));
Until ECMAScript 5 has wide adoption in browsers, you could use your own implementation of Function.prototype.bind:
function bind(func, thisObj) {
return function() {
func.apply(thisObj, arguments);
};
}
events.attach(events.NEW_TASK, bind(this.update, this));
You could store the reference of "this" in a local variable and try calling the function using it
var that = this;
events.attach( events.NEW_TASK, function( ) { that.update(); } );
Related
I need to access a React component within a function nested within a a handler function.
When in a nested function this does not give a reference to the component
so I explicitly declare it instead of doing the usual this.setState(...)
Is there a cleaner way to do this than my following code?
React.createClass({
uploadImage : function() {
...
var componentReference = this
xhrRequest.onreadystatechange = function()
{if(request.readyState==4) {
componentReference.setState({uploadStatus: request.status});
}
}
This is just how JavaScript works:
You'll have to keep a reference to whatever you want to access in a function that's called in a different context (like you implemented it with your variable)…
or
…bind what this points to at the time the function is called. There's no need to use a library (like lodash) because ECMAScript 5.1 has his built-in, and you can use it in any recent-ish browser (Chrome 7+, Firefox 4+, IE 9+, Safari 5.1+), see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
If you need to support older browsers, you can implement a bind function yourself, see for example Underscore.js' Function.prototype.bind implementation: http://underscorejs.org/#bind
The most idiomatic way to do this is with the native .bind() method, no libraries required:
React.createClass({
uploadImage : function() {
// ...
xhrRequest.onreadystatechange = function() {
if(request.readyState == 4) {
this.setState({ uploadStatus: request.status });
}
}.bind(this)
// ...
}
})
.bind(this) sets the context of this inside the .onreadystatechange closure. It's a bit like passing the scope to the inner function.
If you are using e.g. lodash, you can do it like this:
xhrRequest.onreadystatechange = _.bind(function () {
if ( request.readyState == 4 ) {
this.setState({ uploadStatus: request.status });
}
}, this);
Or, if you use jQuery, you can proxy.
You can simply use the Function bind method in plain old JS like function() {}.bind(this) and then "this" inside your function will be your React component.
You can see the definition of bind() here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
You could put the onreadystatechange handler on the “class” itself, profiting from React’s auto binding.
You could also keep a reference to setState, but I’m not sure whether that’s auto-bound as well. Try it out.
Last but not least, since you are probably using a transpiler anyway, you could use an ES6 arrow function. Arrow functions use the “outer” value of this (lexical this)
I started recently to learn patterns in JavaScript and trying to understand how it is working by simulating "classes". So most probably this error is coming because of a bad understanding of how it is executed.
I have the following general class:
var Collection = (function(){
function Collection(){
this.collection = {};
}
var p = Collection.prototype;
p.callback = function(data){collection = data;}
return Collection;
})();
API is a singleton class. Get_Data method will execute some Ajax call but so far it is just an object. The singleton class is as follow:
var WebService = (function(){
var instance;
var init = function(){
return {
get_data: function(callback){
// Dump data without accessing the Server
callback({'id':1234, 'data':"hello world"})
}
}
}
return {
get_instance: function(){
if(!instance)
instance = init();
return instance;
}
}
})();
At some point I create a Collection class and get some data from some source (it will be from a web service but so far I'm just using an object).
var collection_objects = new Collection();
var api = WebService.get_instance();
api.get_data(collection_objects.callback);
I'm using a callback method (name callback for simplicity) in the collection data that should update the model data. My issue is that in the callback I'm not accessing to the collection property. I'm actually creating a new object called collection. At some point, I think that I should use this, but since I will use an Ajax call, I will have to save this in another variable commonly known as self. That's the theory I have read so far, but where do I have to use the self? Is that the approach?
At some point, I think that I should use "this", but since I will use
an Ajax call, I will have to save "this" in another vble commonly
known as "self"
That's a trick to overcome this problem in javascript. In my opinion, that's not so good as the function is tightly coupled to a variable outside the current context.
A better solution is to use .bind to bind the context to be your collection_objects. Like this:
api.get_data(collection_objects.callback.bind(collection_objects));
And use this in your callback, in this case, this is the collection_objects instead of the global window object
p.callback = function(data){ this.collection = data;}
In general, a method should not be concerned about how it's called. It's very unintuitive and bad if this inside Collection's prototype method does not refer to a Collection instance. Therefore, we should call the function in the context of a Collection instance. To avoid forgetting to use .bind, we could try another solution with call or apply:
get_data: function(context,callback){
// Dump data without accessing the Server
callback.call(context,{'id':1234, 'data':"hello world"});
}
And use it like this:
api.get_data(collection_objects,collection_objects.callback);
Internally, .bind uses something like call or apply to achieve the goal.
Function.prototype.bind = function(){
var fn=this,args=Array.prototype.slice.call(arguments),object=args.shift();
return function(){
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
This code is extracted from the book: Secret of the javascript ninja. This code could be used as a polyfill in case the browser does not support .bind method natively.
So I have most of my functions and variables organized into small object-based modules, like so:
module1: {
someVariable: false,
someFunction: function(e) {
do some stuff using someVariable
},
someFunction2: function(e) {
do some other stuff
}
}
And I call these functions as callbacks during various events, like so:
$(function() {
$('.thing').on('mouseenter', module1.someFunction);
}
Now, from within someFunction, I would expect the 'this' keyword to refer to the object in which the function is contained. Instead, it refers to the DOM element that triggered the event that fires the function. Is there anyway I can get access to, say the someVariable variable in the function's containing object other than writing module1.someVariable?
The shortest answer is to try this:
$(function() {
$('.thing').on('mouseenter', function(e) {
module1.someFunction(e);
});
}
The 'this' value is only set to the object the method is attached to if the method is invoked directly on the object:
module1.someFunction(); // direct invocation, 'this' will be set properly
var tempFunc = module1.someFunction;
tempFunc(); // the function was removed first. 'this' will NOT be set
In your case, you are pealing the method off of the object and handing it to an event handler. The event handler doesn't know about the object and doesn't perform a direct invocation.
In fact, the event handler explicitly overrides the context because that is how the jQuery API is defined. You have to explicitly override it back if you want the behavior you're talking about.
Using a library like underscore.js you could also bind the function as you pass it off to the event handler.
$(function() {
$('.thing').on('mouseenter', _.bind(module1.someFunction, module1));
}
I believe that Object.bind is supposed to be natively supported in the future with no libraries, but you can't yet rely on the older browsers to support it.
For a particular listener in my application, I'm using the following code for scope-busting purposes:
// this is all in a prototype of MyClass
var self = this;
myElement.addEventListener("stuff", function(e){self.doStuff(e)});
That will get doStuff to have the desired this binding.
The problem shows up when I try to removeEventListener. I suppose it's because the native function signatures must be different?
// in a different prototype of MyClass
var self = this;
myElement.removeEventListener("stuff", function(e){self.doStuff(e)}); // doesn't work
If I make a separate function that contains all of my scope-busting code, then the this binding in that code will be to the unwanted object of myElement. So the question is: How can I force listener scope and still be able to remove an added event listener?
*note using global / static variables in any way is prohibited due to the nature of the project (otherwise this would be simple!)
This has nothing to do with scope or the way in which you're storing a reference to this. The problem is that removeEventListener expects a reference to a function that's previously been registered as a listener, but you're giving it a brand new function it's never seen before.
You need to do something like this:
var self = this;
var listener = function(e){self.doStuff(e)}
myElement.addEventListener("stuff", listener);
// later
myElement.removeEventListener("stuff", listener);
It doesn't matter that the bodies of your two functions are the same; they're still different functions.
See:
https://developer.mozilla.org/en/DOM/element.removeEventListener
Inline anonymous functions are a very bad practice anyway, so I will suggest the obvious:
function MyClass() {
this.onStuff = this.onStuff.bind(this); //Each instance steals the prototyped function and adds a bound version as their ownProperty
}
MyClass.prototype = {
onStuff: function (e) { //Prototyped, no instance actually uses this very function
this.dostuff()
},
bind: function () {
myElement.addEventListener("stuff", this.onStuff);
},
unbind: function () {
myElement.removeEventListener("stuff", this.onStuff);
}
}
see removeEventListener on anonymous functions in JavaScript
You can't removeEventListener as your using an anonymous function.
Is it possible to run the method on an existing object on timeout of window.setInterval method. I can emulate the same by having some global variable and calling the method of this global variable in setInterval, but i wanted to know if this is possible using the method directly.
Best Regards,
Keshav
Yes, you can do this. You need a helper function to make a new function that has your existing object "bound":
var someRandomObject = {
someMethod: function() {
// ... whatever
},
// ...
};
// this is a "toy" version of "bind"
function bind(object, method) {
return function() {
method.call(object);
};
}
var interval = setInterval(bind(someRandomObject, someRandomObject.someMethod), 1000);
Now when the interval timer calls your method ("someMethod"), the "this" pointer will reference the object.
That version of "bind" is simplified. Libraries like Prototype, Functional, jQuery, etc generally provide more robust versions. Additionally, the "bind" function will be a native part of Javascript someday — it already is in some browsers.