'this' scope in typescript callback functi

2020-04-17 04:50发布

问题:

I cannot understand how 'this' context works in typescript. I cannot access class members in methods. Below is my code

class adopterDetailCtrl {
    public adopter: IAdopter;
    public $router: any;

    static $inject = ['app.common.services.AdopterService'];
    constructor(private adopterService: app.common.services.IAdopterServices) {
        this.adopter = null;
    }

    $routerOnActivate(next) {
        if (next.params.id > 0) {
            this.getAdopterById(next.params.id);
        }
    }

    getAdopterById(adopterId: number): void {
        var AdopterList = this.adopterService.getAdopterById();
        AdopterList.query({ id: adopterId }, (data: adopter.IAdopter[]) => {
            this.adopter = data[0];//this.adopter is undefined here. this refers to 'window'
        });
    }

    setAdopter(data: IAdopter) {
        this.adopter = data;//can access this.adopter
    }
}

回答1:

The this context is just the same in typescript as it as in javascript, as the code you actually run is the compiled javascript that the typescript compiler outputs.

In javascript you have two ways to deal with this issue:

  1. Use the arrow function
  2. Use the Function.prototype.bind function

You are probably passing getAdopterById as a callback, if that's the case then it will be easy to solve using bind:

let myobj = new adopterDetailCtrl(...);

...

someFunction(myobj.getAdopterById.bind(myobj));

You can also modify the reference for the method of the instance in the ctor:

(1)

class adopterDetailCtrl {
    public adopter: IAdopter;
    public $router: any;

    static $inject = ['app.common.services.AdopterService'];
    constructor(private adopterService: app.common.services.IAdopterServices) {
        this.adopter = null;

        this.getAdopterById = (adopterId: number) => {
            var AdopterList = this.adopterService.getAdopterById();
            AdopterList.query({ id: adopterId }, (data: adopter.IAdopter[]) => {
                this.adopter = data[0];//this.adopter is undefined here. this refers to 'window'
            });
        }
    }

    $routerOnActivate(next) {
        if (next.params.id > 0) {
            this.getAdopterById(next.params.id);
        }
    }

    getAdopterById: (adopterId: number) => void;

    setAdopter(data: IAdopter) {
        this.adopter = data;//can access this.adopter
    }
}

Notice that the method declaration is empty and the implementation is set in the ctor using the arrow function.

(2)

class adopterDetailCtrl {
    public adopter: IAdopter;
    public $router: any;

    static $inject = ['app.common.services.AdopterService'];
    constructor(private adopterService: app.common.services.IAdopterServices) {
        this.adopter = null;

        this.getAdopterById = this.getAdopterById.bind(this);
    }

    $routerOnActivate(next) {
        if (next.params.id > 0) {
            this.getAdopterById(next.params.id);
        }
    }

    getAdopterById(adopterId: number): void {
        var AdopterList = this.adopterService.getAdopterById();
        AdopterList.query({ id: adopterId }, (data: adopter.IAdopter[]) => {
            this.adopter = data[0];//this.adopter is undefined here. this refers to 'window'
        });
    }

    setAdopter(data: IAdopter) {
        this.adopter = data;//can access this.adopter
    }
}

Here in the ctor you reassign the bound this.getAdopterById.bind(this) to this.getAdopterById.

In both of these cases you can freely pass the getAdopterById method as a callback and not worrying about the scope of this.

Another note on the arrow functions is that this is a new feature in ES6, and if you don't choose the ES6 target in your compilation options then the compiler won't actually use this notation but instead will convert this:

class A {
    private x: number;

    fn(): void {
        setTimeout(() => console.log(this.x), 1);
    }
}

To:

var A = (function () {
    function A() {
    }
    A.prototype.fn = function () {
        var _this = this;
        setTimeout(function () { return console.log(_this.x); }, 1);
    };
    return A;
}());

This way the scope of this is saved in _this and in the callback function _this.x is used instead of this.x.