简体   繁体   中英

Working of a Autobind Decorator in typescript?

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 bind this 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM