Tricky JS Question
- 7 minsSaw this JavaScript interview question, it seems quite straightforward at the beginning, but it gets quite tricky along the way. Here is the question:
/*
1. Convert the following code to ES6 syntax
2. Return the value after 2 seconds
*/
var john = {
name: "John Doe",
balance: 1500,
deduct: function(amount){
this.balance = this.balance - amount
return this.name + ' has a balance of ' + this.amount
}
}
console.log(john.deduct(200)) // John Doe has a balance of 1300
By looking at the question, on the last line of code, it should log out John Doe has a blance of 1300
. Now let’s go ahead to rewrite it with ES6 syntax.
1. Convert to ES6
const john = {
name: 'John Doe',
balance: 1500,
deduct: amount => {
this.balance = this.balance - amount
return this.name + ' has balance of ' + this.amount
}
}
console.log(john.deduct(200))
// undefined has a balance of NaN
Now, if we run the code, what we got, assuming without any linters, from the console would be undefined has a balance of NaN
. Note we changed the function
keyword to =>
syntax on line 4(I wish Markdown has line number feature ), and it’s not working as expected. What caused this?
Becuase when using
=>
function, it created a lexcial scope, thus, keyword this
is only referring to the inner scope, and the values, which we needed, were only avilable in the outer scope.
this
is a tricky JavaScript concpet, let’s take a look at what MDN has to say:
So, to make it work, we have to use this
to refer its value from outer scope, we can do it by eliminate =>
, and short the line of code like follow:
const john = {
name: 'John Doe',
balance: 1500,
deduct(amount) {
this.balance = this.balance - amount
return `${this.name} has a balance of ${this.balance}`
}
}
console.log(john.deduct(200))
// John Doe has a balance of 1300
Question 1 solved. Let’s look at the second question.
2. Return the value after 2s
I could simply wrap the entire console.log
into a setTimeout
function, that, however, is probabbly not what an interviewer wants to hear.
const john = {
name: 'John Doe',
balance: 1500,
deduct(amount) {
this.balance = this.balance - amount
return `${this.name} has a balance of ${this.balance}`
}
}
setTimeout(() => {
console.log(john.deduct(200))
}, 2000)
What if I move the setTimeout
function inside the deduct
function?
const john = {
name: 'John Doe',
balance: 1500,
deduct(amount) {
setTimeout(() => {
this.balance = this.balance - amount
return `${this.name} has a balance of ${this.balance}`
}, 2000)
}
}
console.log(john.deduct(200))
// undefined
Now, the console says undefined
. I am now pretty sure this is what the interviewer wants to see. Why it’s showing undefined
then? Becuase the
return
statement on line 7 is returning the value of the setTimeout
function not the deduct
function. By understanding this, we shall move the return
statement outside of the setTimeout
function.
Let’s take a look at the MDN return
statment description:
To make it happen, we could move return
statement outside of setTimeout function and have it return
a new Promise
:
const john = {
name: 'John Doe',
balance: 1500,
deduct(amount) {
return new Promise((res) => {
setTimeout(() => {
this.balance = this.balance - amount
res(`${this.name} has a balance of ${this.balance}`)
}, 2000)
})
}
}
john.deduct(200).then(message => console.log(message))
Let’s push the code a bit further, regardless of the sleep
function, the john{}
itself is a lot more readable comparing the code above. And the sleep
function just takes a time
as an argument, and return a Promise
which resloves on setTimeout
. Addtionally, sleep function can be a misc function that seperated out from this file, that makes this code file even cleaner.
Most other programming languages have built-in sleep
functions, JavaScript doesn’t come with one, but we can make one can make one with Promise
.
const sleep = time => new Promise(res => setTimeout(res, time))
const john = {
name: 'John Doe',
balance: 1500,
async deduct(amount) {
await sleep(2000)
this.balance = this.balance - amount
return `${this.name} has a balance of ${this.balance}`
}
}
john.deduct(200).then(console.log)
// John Doe has a balance of 1300
Conclusion
Have a solid understanding of lexcial scope, this
keyword and Promises
are quite curcial.