问题
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