Solutions for binding JS objects for callbacks - javascript

I find that it's very common to have code like (this is TypeScript, but the equivalent JS is fairly obvious and this is really a JS problem, although one that TS could solve):
class Foo {
someField;
someMethod() {
doSomethingTakingACallback(function() {
this.someField; // Fails because `this` is not the instance of `Foo`
});
}
}
The solution, of course, is to use Function.bind() like so:
someMethod() {
doSomethingTakingACallback(function() {
this.someField; // Works as expected
}.bind(this));
}
Now the problem is that I have callbacks that must access the object instance a lot (ie, I need to be calling bind(this) on many callbacks). I've now wasted more time on bugs resulting from this than I'd like to admit. Is there some alternative? Am I missing some easier way to do this? Is there any reason that this is the default behavior as opposed to the function that we get from calling bind(this)?
One solution I know of is to do something like var me = this and then call me.someField (etc). It's a little nicer looking when I have many callbacks or nested callbacks. Although then I lose out on the iconicness of this, which I feel makes it the most clear where the member is located (on the class that I'm writing).

Typescript and ES6/ES2015 both support the "fat arrow function" syntax, which allows you to use this the way you do in most other languages - to refer to the class instance object.
e.g.
someMethod() {
doSomethingTakingACallback(() => {
this.someField;
});
}
This compiles to the following code:
Foo.prototype.someMethod = function () {
var _this = this;
doSomethingTakingACallback(function () {
_this.someField;
});
};

ES6 Arrow functions keep the this of the surrounding scope and thus do not need binding.
ES7 will (possibly) have the :: bind operator

Assign this to another variable outside of your callback method and use that.
class Foo {
someField : any;
someMethod() {
var that = this;
doSomethingTakingACallback(function () {
// use that.someField;
});
}
}

Related

Declare class in function argument in Javascript

I have please encountered the following example in a book of a declaration of a class inside a function argument, even though I have searched thoroughly online, could not find an explanation, what it does, what is declared, what is passed into the function, and when should I use it if at all (perhaps its a bad practice ?).
Thank you for your help and guidance !
function f(parameter1) {
console.log(parameter1);
}
class ClassA {
}
f(class extends ClassA {
render() {
}
});
In javascript, class is just a syntax sugar for function, and just like function it's a first class value, which can be assigned, passed around etc.
let a = class {
foo() {
console.log('hey')
}
};
new a().foo()
//
function x(klass) {
new klass().bar()
}
x(class {
bar() {
console.log(2)
}
})
This is defining an anonymous class (in Java terminology... not sure if that is the correct name for JS). This example is pretty nonsensical. I could see it being used if the function f was accepting a class definition and then instantiated an object using it and proceeded to call methods on the object and perhaps return it. So it could be used in a general factory function.

Using this vs using variable in JS

I am having tough time figuring out when should we preferably use this keyword and when shouldn't we use it..
For example, I was earlier doing something like this.
let socket;
class something extends Component {
componentDidMount() {
socket = openSocket('https://coincap.io');
}
componentDidUpdate() {
socket.on('trades', (tradeMsg) => {
}
componentWillUnmount() {
this.socket.disconnect();
}
then someone re-structured my code did something like
class something extends Component {
componentDidMount() {
this.socket = openSocket('https://coincap.io');
}
componentDidUpdate() {
this.socket.on('trades', (tradeMsg) => {
}
componentWillUnmount() {
this.socket.disconnect();
}
[Question:] While, Both the code works and do the job, I am perplexed between understanding which methods should we use and why did he used this keyword?
You are dealing with ES6, which takes out a lot of ambiguity pertaining to the this keyword in the language.
Quite simply, this means (in the more traditional sense) the current scope; however, in JavaScript, this means the object calling the function as opposed to the current scope.
As mentioned previously, ES6 takes out a lot of complexity regarding variable hoisting and the likes.
Coming to your question, the reason it was refactored to this.something was because when your class is called (now, remember, JavaScript follows prototypal inheritance and is a first-class language; meaning that function prototypes are the class bases and functions can be passed as parameters) it will act like a function and the this keyword will refer to the object or the context where it was called.
If we have the following bit of code:
let foo = 'bar';
const fn = () => { console.log( this.foo ); }
fn();
The execution context would be window and the foo variable would be window.foo; hence, this.foo would translate to window.foo.
Similarly, your code will be transpiled something like:
var socket = new Socket();
function className() {}
className.prototype.someFunction = function() {
console.log( socket );
}
Here, the this keyword would make sure that the current context is used instead of local variables which you might use within your function.
I hope this makes it clear. Here is a great article to understand the this keyword! :)
The problem isn't the difference between this and a variable per se. It's that this refers to class instance and allows to have multiple class instances and thus multiple socket instances, while socket variable refers to specific socket instance and will be overridden in subsequent component instances.
The component will malfunction if there's more than one component instance at time, because socket variable refers to latest openSocket('https://coincap.io') result.
Also, this
let socket;
class something extends Component {
componentDidMount() {
socket = openSocket('https://coincap.io');
}
componentDidUpdate() {
socket.on('trades', (tradeMsg) => {
}
componentWillUnmount() {
this.socket.disconnect();
}
will result in error on component unmount because there's no this.socket.

Eventhandler-function can't use other (helper)functions

Im trying to build an app with Cytoscape.js and Angular2+.
The Core part take place in a single service (cy.service).
I add all Eventlisteners in a single function and call it after the cy init.
eg:
initEventlisteners(){
cy.on('tap', this.handler);
cy.on(...);
...
}
handler(event: any){
console.log('something');
}
If I wrap console.log in a helper-function like that:
helper(){
console.log('something');
)
And use it in the handler-function
handler(event:any){
this.helper();
}
It calls: helper() is not a function.
Thats a big problem that a cant use other functions inside the handler.
Any ideas how to solve that problem ?
That's because with binding like this cy.on('tap', this.handler); you are loosing scope of variable. You should use it in this way cy.on('tap', (event) => { this.handler(event) }); (if it's passing any event object) or cy.on('tap', this.handler.bind(this));.
The frist approach with () => { } is called arrow function, which preserve current scope of variables. It's a new feature of ES6 specification. However you don't have to be afraid, angular compiler will convert it to backward compatible to ES5.
The second approach .bind(this) just simply binding this method to current scope where it's was called.

Returning from a callback function

I'm writing a basic wrapper for the Webkit Storage API (the new version of this API) for me to use. My current problem with it is the queryUsageAndQuota() function.
My wrapper around that particular function originally looked like this:
self.pers.getQuota = function() {
self.pers.queryUsageAndQuota(function(usage,quota) {
return quota;
});
}
// self is a reference to the top level of this 'wrapper'
Hence, I would have called var quota = self.pers.getQuota(); and had the quota returned.
After that failed, I had a look at this question and changed it so I used a user-defined callback instead. Now the function is this:
self.pers.getQuota = function(callback) {
self.pers.queryUsageAndQuota(function(usage,quota) {
callback.call(quota);
});
}
However, executing this line:
self.pers.getQuota(function(quota) {
console.log(quota);
});
shows undefined in the log. I can't figure out why, because when I execute the underlying function navigator.webkitPersistentStorage.queryUsageAndQuota(), I get the right number out of it.
.call doesn't work like you think it does. Instead do callback.call(null, quota).
The actual signature for both Function.prototype.call (and similarly for apply) is
Function.prototype.call(valueOfThis, valueOfParam1, valueOfParam2, ...);
This is also why I often say that the this keyword in javascript is stupid and you should avoid using it. It simply is another parameter like all the others, You just don't get to specify a name for it. It's a vestige of javascript's creation when Netscape mandated that this Scheme-based language look like Java.
Incidentally, you could use the third way of invoking methods here. Simply
callback(quota);
This however has the side effect of making javascript take a guess at what you want this to be (the global window object in this case). As long as you don't use this, the simple syntax works great.
If you actually really want to return a value rather than passing a callback look into javascript promises. I can't tell you which one to use without knowing what environment you in and what libraries you're using but jQuery has a good implementation in the Deferred object
Try
self.pers.getQuota = function(callback) {
self.pers.queryUsageAndQuota(function(usage,quota) {
callback(quota);
});
}
Or
self.pers.getQuota = function(callback) {
self.pers.queryUsageAndQuota(function(usage,quota) {
callback.call(/* an object as 'this'*/, quota);
});
}
Function.prototype.call()
Or
self.pers.getQuota = function(callback) {
self.pers.queryUsageAndQuota(function(usage,quota) {
callback.apply(/* an object as 'this'*/, [quota]);
});
}
Function.prototype.apply()

Expecting the right calling context (this) in the JavaScript 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

Categories