Hello I am curious about the working of the decorator in Typescript for binding 'this' to functions in Typescript.
function autoBind(
target:any,
methodName:String,
descriptor:PropertyDescriptor
){
console.log("Calling Decorator");
const originalMethod = descriptor.value;
const adjustableDescriptor: PropertyDescriptor = {
configurable : true,
get(){
console.log("Calling get");
const boundFn = originalMethod.bind(this);
return boundFn;
}
}
return adjustableDescriptor;
}
class ProjectInput {
constructor(){
this.configure();
}
@autoBind
private submitHandler(event: Event){
console.log("Calling submit handler");
event.preventDefault();
console.log("Submitting data ...");
console.log(this.titleInputElement.value);
}
private configure() {
this.element.addEventListener("submit",this.submitHandler);
}
}
const projInput = new ProjectInput();
What I did:
I created a Class ProjectInput and in the constructor i am calling the configure method so that i can add EventListeners and handle user submit data and for binding 'this' so that it reference the right object. I created a Decorator in typescript that will call automatically as soon as the class declared
Everything is fine but I want to know the behind the scenes of the decorator how it binds the this to the function.
I came here, hoping I'd get a more thorough answer than what I'd be able to find, but at least it encouraged me to dig a little further.
Taken directly from React & Autobinding :
Autobind Decorator is an NPM package which binds methods of a class to the correct instance of
this
, even when the methods are detached. The package uses@autobind
before methods to bindthis
to the correct reference to the component's context.
import autobind from 'autobind-decorator'
class MyComponent extends React.Component {
constructor() {
/* ... */
}
@autobind
addTask(task) {
/* ... */
this.setState({ task });
}
@autobind
myMethod2(task) {
/* ... */
this._anotherBindedMethod(task);
}
render() {
return (
/* ... */
)
}
}
his seems like a simple solution, but I'd rather not have to add a line above each individual method inside each of my React components. Not to worry, Autobind Decorator is smart enough to let us bind all methods inside a component class at once. Like so:
import autobind from 'autobind-decorator'
@autobind
class MyComponent extends React.Component {
constructor() {
/* ... */
}
addTask(task) {
/* ... */
this.setState({ task });
}
/* ... */
}
And just like that,
this
issue is resolved.
Anyway, hope that helps. Reading it a couple times, helped me. Cheers.
Be careful cause in your code getter returns a new function each time is called and this can potentially lead to memory leaks. This happen cause .bind
returns a new function .
So for example if you do .addEventListener('click', this.submitHandler)
you're adding a new function each time. .removeEventListener('click', this.submitHandler)
will not remove nothing cause will not match any listener
You can easily test this is truth like this
const projectInput = new ProjectInput();
projectInput.submitHandler === projectInput.submitHandler; // false
So an easy fix to your code could be this one
function autobindFunction(
target:any,
methodName:String,
descriptor:PropertyDescriptor
){
console.log("Calling Decorator");
if(typeof descriptor.value !== 'function') {throw new TypeError("cannot decorate prop that is not a function")}
const bound = descriptor.value
const adjustableDescriptor: PropertyDescriptor = {
configurable : true,
value: function (...args: any[]) {
return bound.apply(this, args)
}
}
return adjustableDescriptor;
}
class Test {
@autobindFunction
hi() {
console.log("asd")
}
}
const a = new Test()
console.log(a.hi === a.hi) // true
In this way the reference of the function is stable and the function will be always the same
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.