3

同じクラスのメソッド connect でメソッド test を呼び出そうとしています。しかし、私が得ているのは「Uncaught Type Error: Undefined のプロパティ 'test' を読み取れません」です。sftp コールバック内の変数にアクセスするにはどうすればよいですか? なぜそうなのですか?

これが私のコードです:

const SSH2 = require('ssh2').Client;
class SshClient {
  constructor(host, username, password) {
    this.host = host;
    this.username = username;
    this.password = password;
    this.port = 22;
    this.client = null;
  }

  test(testvar) {
    console.log(testvar);
  }

  connect() {
    this.client = new SSH2();
    let client = this.client;
    let username = this.username;
    this.client.connect({
      host: this.host,
      port: this.port,
      username: this.username,
      password: this.password
    });
    this.client.on('ready', function() {
      console.log('Client :: ready', client);
      client.sftp(function(err, sftp) {
        if (err) throw err;
        sftp.readdir('/home/' + username, function(err, list) {
          if (err) throw err;
          console.dir(list);
          this.test('hey');
          client.end();
        });
      });
    });
  }
}

let ssh = new SshClient('host', 'username', 'password');
ssh.connect();

4

2 に答える 2

1

関数をコールバックとして使用する (別の関数に引数として渡す) 場合this、コールバック内の変数はオブジェクトを指していません。

コールバックを個別に定義すると、より明確になります。

class SshClient {
  constructor(host, username, password) {
    //...
  }

  test(testvar) {
    console.log(testvar);
  }

  connect() {
    this.client = new SSH2();
    // ...
    this.client.on('ready', onReadyCallback);
  }
}

function onReadyCallback() {
  console.log('Client :: ready', client);
  client.sftp(sftpCallback);
}

function sftpCallback(err, sftp) {
  if (err) throw err;
  sftp.readdir('/home/' + username, readdirCallback);
}

function readdirCallback(err, list) {
  if (err) throw err;
  console.dir(list);
  this.test('hey'); // It is clear that `this` here doesn't refer
                    // to the SshClient object
  client.end();
});

ご覧のように、this正しくreaddirCallbackないように見えますが、関数はSshClientクラスの一部ではなく、オブジェクトthisを指すことができません。SshClient

最も簡単な解決策は、コード内の変数で行うのと同じことを行い、client追加の変数にusername保存することです。this

  connect() {
    this.client = new SSH2();
    let self = this;
    // ...
    this.client.on('ready', function() {
       // we can use "self" variable here, 
       // as it's avaialbe from the outer scope
       self.client; // we can refer to client and username
       self.username;
       self.test(); // and we can call object methods
    });
  }

もう 1 つの代替手段は、コールバックを別々に保ち、オブジェクトを追加のクロージャーにキャッチすることです。

class SshClient {

  connect() {
    this.client = new SSH2();
    // ...
    this.client.on('ready', getOnReadyCallback(this));
  }
}

function getOnReadyCallback(sshClient) {
  function onReadyCallback() {
    console.log('Client :: ready', sshClient.client);
    sshClient.client.sftp(getSftpCallback(sshClient));
  }
}

他の回答で言及されているアロー関数は、回避策を必要としないため、おそらく最良の解決策ですが、問題の内容とアロー関数がそれを解決する理由を明確に理解する必要があります。

アロー関数式は、関数式よりも構文が短く、独自の this、引数、super、または new.target を持ちません。これらの関数式は、メソッド以外の関数に最適であり、コンストラクターとして使用することはできません。

アロー関数には独自のものがないthisため、アロー関数をコールバックとして使用すると、元のオブジェクトがthis引き続き使用可能になります。

于 2017-11-23T21:37:50.237 に答える