无法在同一 class JS 的另一个方法中调用方法

Not able to call method within another method of the same class JS

我正在尝试在同一个 class 的连接方法中调用方法测试。但我得到的只是 "Uncaught Type Error: Cannot read property 'test' of undefined"。 如何访问 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();

使用 function() { 时,您将进入一个新的环境,这不是您的 class 环境。使用 es6 arrow functions,您可以轻松地将 class 上下文共享到内部函数中。


  this.client.on('ready', () => {
      client.sftp((err, sftp) => {
        if (err) throw err;

        sftp.readdir('/home/' + username, (err, list) => {
          if (err) throw err;

          this.test('hey');

          client.end();
        });
      });
    });

Here 是一篇关于 es6 arrow functions 如何运作以及它们如何影响 this 的好文章。

当您将函数用作回调(将其作为参数传递给另一个函数)时,回调中的 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();
});

如您所见,readdirCallback 中的 this 看起来不再正确,该函数不属于 SshClient class 和 this 不能指向 SshClient 对象。

最简单的解决方案是对代码中的 clientusername 变量执行相同的操作 - 将 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
    });
  }

另一种选择是将回调分开并将您的对象捕获到额外的闭包中:

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));
  }
}

另一个答案中提到的arrow functions可能是最好的解决方案,因为您不需要任何变通方法,但是您必须清楚地了解问题所在以及箭头函数解决问题的原因:

An arrow function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors.

箭头函数没有自己的 this - 因此如果您使用箭头函数作为回调,原始对象的 this 仍然可用。