7 Quick JavaScript Pop Quizzes With Explanations
Milos Protic
Posted on October 31, 2019
originally published on devinduct
Introduction
I believe that learning new things and evaluating the things we know is useful to keep us on track, thus avoiding the situation where we feel outdated. In this article, we will go through some basics of JavaScript. Enjoy!
1. Declarations
Think about the following code, and try to answer what gets logged (and why) without reading the explanation below.
// situation 1
console.log(person);
var person = 'John';
// situation 2
console.log(person);
let person = 'Phill';
// situation 3
console.log(person);
const person = 'Frank';
// situation 4
const person = 'Vanessa';
console.log(person);
person = 'Mike';
console.log(person);
// situation 5
var person = 'John';
let person = 'Mike';
console.log(person);
// situation 6
var person = 'John';
if (person) {
let person = 'Mike';
console.log(person);
}
console.log(person);
Explanation
Situation 1: The expected result here would be to see the text
John
written in the console, but surprisingly, we seeundefined
gets logged instead. Wonder why?
Well, here we can see the classic JavaScript in action. This behavior is called hoisting. Under the hood, the language splits the variable declaration and value assignment into two pieces. The variables are moved to the top, declared with the value set to undefined
(hoisted), regardlessly of where they were initially declared by a developer. It looks something like this:
var person;
console.log(person);
person = 'John';
Situation 2: Here, the result will be a reference error.
Uncaught ReferenceError: Cannot access 'person' before initialization
The error text speaks for itself. Because we used the keyword let
, our variable is hoisted but remained uninitialized, and the error gets thrown informing us that we are trying to access an uninitialized variable. The let
keyword was introduced in ES6 enabling us to use block-scoped variables thus helping us prevent unintended behavior.
Situation 3: Here, we have the same error as in the Situation 2.
The difference is that we've used the keyword const
, thus preventing re-assigning our variable after initialization. This keyword was also introduced in ES6.
Situation 4: In this case, we can see how the keyword
const
is useful and how it can save us from unintentionally re-assigning our variable. In our example, first, we will seeVanessa
written in the console, and then a type error.
Uncaught TypeError: Assignment to constant variable
The usefulness of const
variables grows exponentially with our codebase.
Situation 5: If a variable has already been defined inside a certain scope with the keyword
var
, trying to declare it again with the keywordlet
inside the same scope throws an error.
So, in our example, nothing will be logged and we will see a syntax error.
Uncaught SyntaxError: Identifier 'person' has already been declared
Situation 6: We have a function-scoped variable first and block-scoped variable second. In this case, it doesn't matter if they have the same name/identifier.
In the console, we should see Mike
and John
getting logged, in that order. Why?
Because the keyword let
gives us the block-scoped variables, meaning that they only exist within the scope they are created, in this case within the if...else
statement. The inner variable takes primate over the outer variable and this is the reason why we can use the same identifier.
2. Inheritance
Consider the following classes and try to answer what gets logged and why.
class Person {
constructor() {
this.sayHello = () => {
return 'Hello';
}
}
sayBye() {
return 'Bye';
}
}
class Student extends Person {
sayHello() {
return 'Hello from Student';
}
}
const student = new Student();
console.log(student.sayHello());
Explanation
If you said
Hello
you were right!
Why: Each time we create a new Student
instance, we set the sayHello
property to it to be a function
returning the string Hello
. This is happening in the parent (Person
) class constructor.
Classes are syntactical sugar in JavaScript, and under the hood, in our example, the sayHello
method in the Student
class is defined on the prototype chain. Considering that each time we create an instance of the Student
class we set the sayHello
property to that instance to be a function
returning the string Hello
, we never get to use the function defined on the prototype chain thus we will never see the message Hello from Student
being logged.
3. Object Mutability
Consider the following situations and think of each section output:
// situation 1
const user = {
name: 'John',
surname: 'Doe'
}
user = {
name: 'Mike'
}
console.log(user);
// situation 2
const user = {
name: 'John',
surname: 'Doe'
}
user.name = 'Mike';
console.log(user.name);
// situation 3
const user = {
name: 'John',
surname: 'Doe'
}
const anotherUser = user;
anotherUser.name = 'Mike';
console.log(user.name);
// situation 4
const user = {
name: 'John',
surname: 'Doe',
address: {
street: 'My Street'
}
}
Object.freeze(user);
user.name = 'Mike';
user.address.street = 'My Different Street';
console.log(user.name);
console.log(user.address.street);
Explanation
Situation 1: Here, as we learned in the previous section, we are trying to re-assign a
const
variable that is not permitted, therefore, we will get a type error
The result in our console will be the following text:
Uncaught TypeError: Assignment to constant variable
Situation 2: In this scenario, we have different behavior even though we are modifying the variable declared with the keyword
const
. The difference is that we are changing the object property and not its reference, and this is allowed onconst
object variables.
The result in the console should be the word Mike
.
Situation 3: By assigning the
user
toanotherUser
variable we are sharing the reference, or memory location if you like, between them. In other words, both of them will point to the same object in memory, therefore, changing the property on one object will reflect the change on the other.
The result in the console should be Mike
.
Situation 4: Here, we are using
Object.freeze
method to provide the functionality that was lacking in the previous scenario (situation 3). By using this method, we can freeze our object thus not allowing for its property values to change. But there is a catch. It will only do a shallow freeze, meaning that it will not protect the deep property updates. This is the reason why we are able to mutate the street property while the name property will remain unchanged.
The output in the console should be the words John
and My Different Street
, in that order.
4. Arrow Function
What will be logged and why after running the following snippet:
const student = {
school: 'My School',
fullName: 'John Doe',
printName: () => {
console.log(this.fullName);
},
printSchool: function () {
console.log(this.school);
}
};
student.printName();
student.printSchool();
Explanation
The output in our console will be undefined
and My School
, in that order.
If you are coming from the old school, you will probably be familiar with the following syntax:
var me = this;
// or
var self = this;
// ...
// ...
// somewhere deep...
// me.doSomething();
You can think of me
or self
variable as the parent scope accessible for every nested function created within.
When using arrow functions, this is done for us and we no longer need to store the this
reference in order to have access to it somewhere deeper in our code. Arrow functions do not bind their own this, instead, they inherit the one from the parent scope and this is the reason why we have undefined
logged after invoking the printName
function.
5. Destructuring
Check out the destructuring below and think about what will be logged. Is the given syntax allowed or an error will be thrown?
const rawUser = {
name: 'John',
surname: 'Doe',
email: 'john@doe.com',
displayName: 'SuperCoolJohn',
joined: '2016-05-05',
image: 'path-to-the-image',
followers: 45
}
let user = {}, userDetails = {};
({ name: user.name, surname: user.surname, ...userDetails } = rawUser);
console.log(user);
console.log(userDetails);
Explanation
Although a bit out of the box, the syntax above is allowed and it doesn't throw an error! Pretty neat, right?
Most of us are not accustomed to the right side of expression looking like that...I mean, only the left side should contain dot syntax...or not? :)
All jokes to the side, the syntax above is powerful and enables us to easily split any object into two more specific ones as shown in the example above.
The console output is:
// {name: "John", surname: "Doe"}
// {email: "john@doe.com", displayName: "SuperCoolJohn", joined: "2016-05-05", image: "path-to-the-image", followers: 45}
6. Async/Await
What will be logged after the following immediate function gets invoked?
(async () => {
let result = 'Some Data';
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve('Some data retrieved from the server'), 2000);
});
result = await promise;
console.log(result);
})();
Explanation
If you said Some data retrieved from the server
after 2 seconds, you were right!
The code is paused until the promise gets resolved. After two seconds, it continues and logs the given text. This means that the JavaScript engine will literally wait until the asynchronous operation is completed. The async/await
approach is, let's say, syntactic sugar to get the promise result. Some might say, a more readable way than promise.then
.
7. The Return Statement
const multiplyByTwo = (x) => {
return
{
result: x * 2
};
}
console.log(multiplyByTwo(2));
Explanation
If you said {result: 4}
, well, you were wrong. The output is undefined
. But don't be so harsh on yourself, it bugged me too, considering that I write C# as well and this is not a problem there.
The code above will return undefined
due to Automatic Semicolon Insertion which says that no line terminator is allowed between the return keyword and the expression
The solution would be to fix our function and write it in one of the following ways:
const multiplyByTwo = (x) => {
return {
result: x * 2
};
}
or
const multiplyByTwo = (x) => {
return (
{
result: x * 2
}
);
}
Conclusion
That's it for this session. I hope you liked it and if so, follow me on twitter, or subscribe to devinduct to stay tuned.
Posted on October 31, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 21, 2024