admin管理员组

文章数量:1287484

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

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

Share Improve this question asked Nov 23, 2017 at 13:04 juldepoljuldepol 1431 gold badge3 silver badges9 bronze badges 1
  • Use : var root = self at the begging of class. After in sub methods use root.test() – Nikola Lukic Commented Nov 23, 2017 at 13:09
Add a ment  | 

2 Answers 2

Reset to default 8

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.

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.

本文标签: javascriptNot able to call method within another method of the same class JSStack Overflow