3

我试图在同一个类的方法连接中调用方法测试。但我得到的只是“未捕获的类型错误:无法读取未定义的属性‘测试’”。如何访问 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

5

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


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

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

于 2017-11-23T13:15:02.040 回答
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();
});

如您所见,thisreaddirCallback看起来不正确的情况下,该函数不是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
    });
  }

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

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、arguments、super 或 new.target。这些函数表达式最适合非方法函数,它们不能用作构造函数。

箭头函数没有自己的this- 所以如果你使用箭头函数作为回调,原始对象的this保持可用。

于 2017-11-23T21:37:50.237 回答