Recently, I experienced my first intensive five hour technical assessment. It was a nerve-racking experience that left me mentally exhausted but at the same time, proud that I had completed it. It made me realize the significance of having fundamentals down when it comes to technical assessments. Understanding the basics of your language of choice can be the guiding force towards tackling a technical question or problem.
The assessment was divided into timed sections, starting with technical questioning from two interviewers, followed by the technical assessment itself, then a walk through of my work along with further technical questioning, and finally an interview with a product manager.
The main technical assessment involved building out a feature with logic that pulled data from static json. I was told that it didn't matter if I didn't finish the feature, as long as I talked about my intentions and next steps to the interviewers.
When the time came, I was left with a half completed project as expected. I was so mentally drained that I felt myself getting shaky at explaining my work.
The interviewers were helpful by asking me what technical issues I faced. I felt myself ranting rather than using technical language to explain my difficulties, feeling I was getting nowhere with my explanation. It wasn't until one of the interviewers asked "Is this a problem with abstraction? Inheritance? What do you think is going on?" .
Those words brought me out of my brain fog, and pointed to the basics of JavaScript that I could understand, The principles of Object Oriented Programming. I felt like I was being pushed in the right direction by my interviewer. I began to explain abstraction and how I was struggling with it within my code which led me grow upon other concepts of OOP within my project.
I realized that even though I was lost, I had those pillars of knowledge to guide me back into a technical mindset. Despite having a moment of struggle, I was able to display my strong fundamentals without letting my nerves get the best of me. Having the basics of OOP in your vocabulary allows you to draw from examples in your code and show that even if you're stuck, you're still able to communicate your technical skills effectively.
Having this moment allowed me to further understand why my coding teacher was always hammering in the pillars of OOP to us. So even though I might have covered this topic before, I wanted to revisit this concept with examples to help other prepare for their first technical assessment!
To start off, lets ask ourselves:
What is an object?
An object is a collection of properties and functions that describe any existing entity.
An example of an object can be a dog, it has properties like:
name, breed, color. Properties allow us to identify our objects and differentiate them from other objects.
With our dog, we're able to give it different functions(methods) like fetch(), or speak() that return certain actions.
So, how do we create new objects in JavaScript?
There are different methods you can use to create a new object in JavaScript, but for now, lets just create an empty object.
const dog1 = {}; //using object literals
const dog2 = Object.create(null); //using Object.create()
const dog3 = new Object(null); //using the "new" keyword
Once we have our object created, let's give it some properties and methods.
const dog = {
name: 'Sophie',
breed: 'German Shepard',
sayHello: () => {
console.log('Bark!');
}
}
dog.sayHello(); //'Bark!'
Above, we are creating our dog object by using an object literal
Inside the object literal, we define properties and methods in a syntax known as "key:value" notation
"name" and "breed" are the object properties that are in "key:value" notation
sayHello is an object method that returns an action which is console.log('Bark!');
the last line is calling the object method using the dot(.) notation to access properties and methods within the object literal
Now we know how to create objects with properties, methods, and access them. Let incorporate them into classes.
JavaScript classes are known as the "blueprint" or template for creating new objects.
class Dog {
name;
breed;
constructor(name, breed) {
this.name = name;
this.breed = breed;
}
sayHello = () => console.log(`$(this.name) says hi!`);
}
const myDog = new Dog('Fletcher','Pit bull');
myDog.sayHello(); //'Fletcher says hi!'
To refresh ourselves, classes always start with an uppercase letter.
-within the class above, we have our properties listed
-after it, we have our constructor which we use to assign values to new objects using the "new" keyword
To create object instances in a class, we use the "new" keyword and the name of the class: new Dog
-In the parenthesis, we identify the values of the properties defined in the constructor to assign them to the new Dog object:
new Dog('Fletcher','Pit bull');
Let's move onto another important topic:
Inheritance!
It's not too far off from the non technical definition of characteristics or genes that are passed down from a parent to a child.
In a technical context, inheritance is the concept of creating a new child class, also known as a subclass from an existing parent class or superclass.
The child class/subclass can inherit properties and methods of the parent class, or can derive it's own properties and methods.
// parent class
class Mom {
constructor(name) {
this.name = name;
}
greet() {
console.log(`${this.name} says bark!`);
}
}
// inheriting parent class
class Puppy extends Mom {
}
let puppy1 = new Puppy('Fletcher');
puppy1.greet(); //'Fletcher says bark!'
To create a class that will inherit the parent's properties, we use "extends" with the class name. In this case, puppy is the subclass or child class is extending properties from its Mom aka parent class/superclass.
An instance of the puppy object was created using the "new" keyword, and is named Fletcher. Fletcher inherits the mom's bark because when we call puppy1.greet and invoke it with the parenthesis, our return is 'Fletcher says bark!'.
Let's move onto encapsulation!
Encapsulation is a very important pillar of OOP because it gives us the ability to hide and protect data from being accessed from outside of a class. We can guess from the name 'encapsulation', that data is kept inside a capsule, which is essentially our class.
Properties and methods that are encapsulated are known as "private" properties and methods.
For this example, let's say my dog Fletcher wants to refill his prescription for more treats at the Vet. To access his medical file, he needs his fileCode.
class Dog extends Vet {
#fileCode; //private property
constructor(breed, fileCode) {
super(breed);
this.#fileCode = fileCode;
}
unlock = () => {
console.log(`Unlocked using ${this.#fileCode}`)
}
const myDog = new Dog('Fletcher', 9119);
myDog.unlock(); // 'Unlocked using 9119'
console.log(myDog.#fileCode); //throws error
}
In the class, Dog is a child class of the parent class Vet. We specify values for the properties defined inside the constructor to later assign to new objects.
To declare private properties or private methods, add a "#" at the beginning of their name to make it inaccessible outside of the class.
Another example is using function based syntax:
class Dog {
setAttr(breed, age){
this.breed = breed;
this.age = age;
}
getBreed() {
console.log(this.breed)
}
getAge() {
console.log(this.age)
}
}
const d = new Dog()
d.setAttr("Fletcher", 3) //undefined
d.getBreed() //undefined
d.getAge() //undefined
Now that we have a familiar with encapsulation, let's take a look at Abstraction.
Abstraction is what helps make our code reusable by hiding complex details and showing simple ones. It helps the code to be more understandable, readable, and help the security of your app.
Let's say Fletcher is still trying to hack his medical chart to get prescribed more treats.
function Dog(name, age) {
this.name = name;
this.age = age;
const pin = "pin"
const findPin = function() {
console.log(pin+ ' is private')
}
this.getPin = function() {
console.log(name, age)
findPin()
}
}
const myDog = new Dog("Fletcher", 3)
myDog.getPin(); //undefined
Above, we have two functions inside the main function: findPin and getPin. Abstraction is achieved by hiding the variable ‘pin’ and function ‘findPin’ from the outside and it is only accessible from getPin function.
And finally,
Polymorphism
From the name we can deduce that polymorphism means to take on multiple forms. But in this case, polymorphism means the same method can be used on different objects.
For example, if Sophie and Fletcher have the same function - fetch(), polymorphism allows to call the same method on different objects.
class Dog {
fetch() {
console.log('jog')
}
}
class Puppy extends Dog {
fetch() {
console.log('run')
}
}
const sophie = new Dog()
const fletcher = new Puppy()
sophie.fetch()
fletcher.fetch()
Above, we can see how the same function is called on different classes.
Takeaways:
OOP Principles in JS:
Inheritance, Encapsulation, Abstraction, and Polymorphism
Even though I'm past being a complete novice, there is still importance to refreshing yourself with the fundamentals of Object Oriented Programming. The more time dedicated to understanding them, it becomes very clear and even intuitive what each of them does when faced with a technical assessment.
PS: If you made it through this long article, congrats. And if you feel a bit overwhelmed, don't worry. It will make sense with time. To make it up to you, here's Fletcher the hacking snacking boi.