Переменные доступа, объявленные компонентом из функции subscribe() RxJS

Я могу использовать this.variable для доступа к переменным в любой части компонента, кроме как внутри функций RxJS, таких как subscribe() или catch().

В приведенном ниже примере я хочу напечатать сообщение после запуска процесса:

import {Component, View} from 'angular2/core';

@Component({
    selector: 'navigator'
})
@View({
    template: './app.component.html',
    styles: ['./app.component.css']
})
export class AppComponent {
    message: string;

    constructor() {
        this.message = 'success';
    }

    doSomething() {
        runTheProcess()
        .subscribe(function(location) {
            console.log(this.message);
        });
    }
}

Когда я запускаю doSomething(), я получаю неопределенное значение. Этот сценарий может быть решен с использованием локальной переменной:

import {Component, View} from 'angular2/core';

@Component({
    selector: 'navigator'
})
@View({
    template: './app.component.html',
    styles: ['./app.component.css']
})
export class AppComponent {
    message: string;

    constructor() {
        this.message = 'success';
    }

    doSomething() {

        // assign it to a local variable
        let message = this.message;

        runTheProcess()
        .subscribe(function(location) {
            console.log(message);
        });
    }
}

Я предполагаю, что это связано с this, однако, почему я не могу получить доступ к this.message внутри subscribe()?

Ответ 1

Это не имеет никакого отношения к rx или angular, и все, что связано с Javascript и Typescript.

Я предполагаю, что вы знакомы с семантикой this в контексте вызовов функций в Javascript (если нет, нет недостатка в объяснениях в Интернете) - Разумеется, эти семантики применяются в первом фрагменте, и единственная причина this.message - undefined внутри subscribe(). Это просто Javascript.

Поскольку мы говорим о Typescript: Функции стрелок представляют собой конструкцию Typescript, предназначенную (частично), чтобы обойти неуклюжесть этой семантики путем лексического захвата значения this, что означает, что this внутри функция стрелки === this из внешнего контекста.

Итак, если вы замените:

.subscribe(function(location) {
        //this != this from outer context 
        console.log(this.message); //prints 'undefined'
    });

:

.subscribe((location) => {
     //this == this from the outer context 
        console.log(this.message); //prints 'success'
    });

Вы получите ожидаемый результат.

Ответ 2

В качестве альтернативы ответу @drewmoore, если вы хотите иметь внешние функции, вы можете сделать:

 .subscribe((location) => dataHandler(location), (error) => errorHandler(error));

 ....

 const dataHandler = (location) => {
     ...
 }

Внешняя функция errorHandler может использоваться в нескольких местах (например, в подписках). Используя функции стрелок as (жирный), ваш код будет захватывать контекст this (как обсуждалось в ответе @Drewmoore).

Чего не хватает, так это умения написать следующее и обработать как функцию стрелки. Следующее работает и передает аргумент неявно. К сожалению, AFAIK, вы не можете охватить this контекст (возможно, используйте bind для достижения этого, хотя это делает код более многословным в целом).

 .subscribe(dataHandler, errorHandler);

Это тааак лаконично! Но, увы, не будет работать, если требуется контекст.