I'm fairly experienced programming but quite new to TypeScript.
Trying to use it with jQuery and immediately ran into the 'this' issue with callbacks (such as $(document).ready.
Using $.proxy() is one way to go, but using TypeScript's arrow (lambda) functions seemed much better. But I only see them used as expressions — that is the entire function is defined inline. I would like to be able to set up arrow functions that could be called as methods of my class, something like (in pseudocode):
class Something {
constructor() {
$(' nav li').click(this.menuClick);
}
private menuClick (and this would be an arrow function to preserve 'this')()=>
// it would call this.anotherMethod()
// plus lots of other things so it won't easily fit inline
private anotherMethod():void {
// do something on the menu click, several lines perhaps,
}
}
I come from an OOP background in AS3, and that's how I was able to do it -- 'this' was readily accessible or it was clear how to access it. I'm keen to use TypeScript to get past the OOP hurdle I'm having with Javascript plain -- but it seems cumbersome (to me) to have to proxy all the jQuery calls (I know there are classes out there to do this for me, but is there a simpler way, with arrow/lambda functions?).
Have patience if I'm not grokking the obvious, but it ain't obvious to me!
Arrow functions don't have their own bindings to this , arguments , or super , and should not be used as methods. Arrow functions cannot be used as constructors.
ES6 version of TypeScript provides an arrow function which is the shorthand syntax for defining the anonymous function, i.e., for function expressions. It omits the function keyword. We can call it fat arrow (because -> is a thin arrow and => is a "fat" arrow). It is also called a Lambda function.
Arrow functions cannot be used to write object methods because, as you have found, since arrow functions close over the this of the lexically enclosing context, the this within the arrow is the one that was current where you defined the object.
You should avoid using arrow functions in class as they won't be the part of prototype and thus not shared by every instance. It is same as giving the same copy of function to every instance.
The reason there isn't a way to do this by default is that it's a tremendous runtime cost in terms of per-instance memory consumption if done automatically. Rather than having one base prototype object with all the functions in it and each class instance pointing to that prototype for its function definitions, you end up having a closure per function for every instance of the class.
There might be better type system support for this in the future, but for now, dealing with this
binding issues is just a tax you have to pay when writing JavaScript of any flavor. Hopefully as things like TypeScript gain momentum, library authors will reduce their usage of this
-stomping (though the DOM will never have that luxury).
The upside is that in TypeScript, it's not a huge amount more code when you do need to re-re-bind this
:
$(' nav li').click(() => this.menuClick());
or
$(' nav li').click(this.menuClick.bind(this));
The short answer:
If you change : $(' nav li').click(this.menuClick);
into : $(' nav li').click(() => this.menuClick());
then this
will be your class 'Something' inside the menuClick
method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With