0

What is the difference between:

export class Test {

  x: any;

  processData = (data) => {
    console.log("This=",this);
    this.x = data; 
    console.log("Loaded.", data);
  }
}

and this:

export class Test {

 x: any;

 processData(data) {
  console.log("This=",this);
  this.x = data; 
  console.log("Loaded.", data);
 }

}

For the last one, in the following code:

 ionViewLoaded() {
   console.log("## ionViewDidLoad");
   this.myApi.getPromise().then(this.processData);
 }

the function ionViewLoaded() does not work because this==null inside processData()

Chocksmith
  • 1,188
  • 2
  • 12
  • 40

1 Answers1

2

this is actually precisely === undefined under strict mode in the second example. The difference is that all arrow functions close over the lexical this value of the scope in which they are created.

In your first example:

export class Test {

  x: {}; // Please stop using any.

  processData = (data) => {
    console.log("This=", this);
    this.x = data; 
    console.log("Loaded.", data);
  }
}

that lexical context is the instance of the enclosing class as you are defining an instance property, not a prototype method, the value of which is an arrow function closed.

In the second example:

export class Test {

  x: {};// Please stop using any.

  processData(data) {
    console.log("This=", this);
    this.x = data; 
    console.log("Loaded.", data);
  }

}

You are creating a method on the class prototype. A method has a dynamically scoped this reference that is bound at invocation time when it is called.

If you call it as object.method(), this will refer to object inside of method for that invocation and that invocation only.

If you call it as method(), that is with no receiving object, and if you running in strict mode, then this will be undefined inside of method for that invocation and that invocation only.

If you are not running in strict mode, this will refer to the global object. For example in a browser it will refer to window. This is very bad. Always run in strict mode.

Code is implicitly in strict mode inside of ES Modules and class bodies. You can set strict mode explicitly by beginning the enclosing file or enclosing function with the 'use strict'; directive prologue.

That is why the code fails in your second example, because this is undefined. In order to pass a method as a callback you need to bind it first. That is to say you would write

ionViewLoaded() {
  console.log("## ionViewDidLoad");
  this.myApi.getPromise().then(this.processData.bind(this));
}

Alternately you could create an arrow function that closed over the instance and the method at use site as in

ionViewLoaded() {
  console.log("## ionViewDidLoad");
  this.myApi.getPromise().then(data => this.processData(data));
}

or just use an arrow function bound to a property as in your first example.

Oh, and if the comments didn't give you the hint, please do not annotate anything with the any type.

I hope that helps.

Aluan Haddad
  • 29,886
  • 8
  • 72
  • 84
  • 1
    It is clear now! Tks! Last question... Why should I stop using "any"? – Chocksmith Mar 23 '17 at 23:57
  • Because it is meaningless. It allows any, no pun intended, operation to, from, on, and by, the value and will never be checked by the type checker. It would be better to have an implicit `any`, that is no type annotation at all, since the effect is the same but you might one day improve it. The implication here is do not use the `--noImplicitAny` flag until you are ready to remove all `any`s from your code. _Explicit_ `any`, a good type does not make. – Aluan Haddad Mar 24 '17 at 00:16
  • Sure thing :) If you do not know what type something should be, a good way to go is to start with the empty type `{}` which can safely hold any value but cannot be used unsafely, and then add members to that type as you need them like `{}`, then later `{ name: string }`. As the type grows more complex, extract an interface definition, `interface Data { name: string; age: number; telephone: string }`, and user that instead. – Aluan Haddad Mar 24 '17 at 00:21