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

只谈情不闲聊 提交于 2019-12-13 16:08:33

问题


I am trying to call method test in method connect of the same class. But all I am getting is "Uncaught Type Error: Cannot read property 'test' of undefined". How do I access any variables inside of sftp callback? Why is it so?

Here is my code:

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

回答1:


When using function() { you are getting into a new context which is not your class context. Using es6 arrow functions, you can easily share your class context into inner 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();
        });
      });
    });

Here is a good article about how es6 arrow functions works and how they affect this.




回答2:


When you use the function as a callback (passing it as an argument to another function), the this variable inside the callback is not pointing to your object.

If you define callbacks separately, it'll be clearer:

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

As you can see, this in the readdirCallback doesn't look right anymore, the function is not a part of the SshClient class and this can not point to the SshClient object.

The simplest solution is to do the same thing you do with client and username variables in your code - save this into additional variable:

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

One more alternative is to keep the callbacks separate and catch your object into additional closure:

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

The arrow functions mentioned in the other answer are probably the best solution as you don't need any workarounds, but you have to clearly understand what the issue is and why arrow functions solve it:

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.

The arrow function doesn't have own this - so if you use arrow function as a callback the original object's this stays available.



来源:https://stackoverflow.com/questions/47456235/not-able-to-call-method-within-another-method-of-the-same-class-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!