将方法用作过滤器的回调函数会影响回调的范围

I ran into an issue in angular where my method loses its scope by being called as a callback function. Can someone explain why this changes to undefined?

@Component({...})
export class SomeClass {
  public status = 'any';

  public filter_account_accesses() {
    console.log( this.status );
    let account_accesses = [...];
    return account_accesses.filter( this.status_filter )
  }
  private status_filter( account_access ): boolean {
    console.log( typeof this );
    // this is undefined!
    return true;
  }
}

PS: I already know I can work around the issue by passing this as an argument. I want to know why the change of scope is happening. And optionally how I can prevent it.

评论
  • wfuga
    wfuga 回复

    Value of this in a function depends on how that function is called. Let's look at a simple example:

    function A() {}
    A.prototype.foo = function() {
      // do something
    }
    
    const a = new A();
    

    If foo is called like this, the value of this inside will be the a object:

    a.foo();
    

    But if you store a reference to the foo function to some other variable, it won't be bound to the a instance anymore and the value of this will be undefined:

    const f = a.foo;
    
    f(); // <- value of this inside is undefined
    

    This is similar to what you're doing in your code snippet: you're passing a reference to the instance method status_filter to the filter function and it's not bound to your instance, that's why when the filter callback is called the value of this inside is undefined.

    You can bind the function to your instance by using bind:

    return account_accesses.filter( this.status_filter.bind(this) )
    

    It will create a copy of your function where this points to your instance.

    Also, I'm recommending you this great and thorough article about how this works in JS: http://dmitrysoshnikov.com/ecmascript/chapter-3-this/

  • oautem
    oautem 回复

    Because your function is called with array filter. There is 2 ways to do it without losing this:

    1. 使用箭头功能
    return account_accesses.filter(value => this.status_filter(value));
    
    1. Using bind
    return account_accesses.filter(this.status_filter.bind(this));