Notes of "Interviewing for frontend engineers" course

Index

This is not a well redacted article. It's a bunch of notes of Jem Young's video Interviewing for front-end engineers at Frontend Masters.

Interviews are half luck. So you're probably gonna fail. But you're not a failure, just mentally prepare for that.

0.501 

First call interview

Posible answers to prepare before the call:

  • What do you do currently?
  • What are some projects you've worked on recently?
  • What are you looking for in your next role?
  • Why do you want to work for __?
  • What is your availability for the next steps?
  • How much do you want to be payed?

Questions I should probably ask:

  • How many steps are in the interview process? How long does it generally take?
  • How big is your engineering team?
  • Which team would I be interviewing for?
  • What is the culture like?
  • What sort of projects would I work on?
  • Is remote/hybrid work allowed?

Prescreen Javascript example questions

  • Difference between let, const, var?

    • const: immutability. Actually you can't point a const to a different pointer. But you can modify the data structure defined in it.
    • let: you can change its pointer, but it's ging to be sticked in whatever scope it is defined.
    • var: hoists to the top of the lexical scope and when you try to access it, if not defined will give you an undefined, not like let and const, that will throw an err.
  • Explain prototypical inheritance

    • everything inherits from the object
  • What this means in JS?

    • the current scope / global context that is available to access
    • if there's no scope is the global window object usually
  • What is the data structure of the DOM?

  • What is a stack and a queue? How would you create this data structures in JS?

    • both data structures
    • stack: lifo
    • queue: fifo
    • you can create them by using a simple js array or a List()
  • How can you tell if an image element is loaded on a page?

    • there's a callback for that on the element. onload fn.
  • What is call() and apply()?

    • a way of changing the scope of the calling function
    • call(a, b, c)
    • apply([a, b, c])
  • What is event delegation and what are the performance tradeoffs?

    • it is also called bubbling
    • having one event defined at the top, and one the event is triggered at some point of the DOM tree, it bubbles up until it's catched
  • What is a worker and when you should use one?

    • Browser api to offload something that requires a high effort to be executed, so we offload that into a different thread to not block the main thread of the browser
    • cause js is single threaded

Expect questions that would be useful in your daily work, is a red flag if you get some random trivia questions.

At home test

  • Make code as readable as possible
  • Do not overcomplicate the code
  • Small commits
  • Do not use too many libraries, or if you do, justify that
  • Add unit testing
  • Ask questions
  • Explain the reasonings with a readme
/*
Hi there! Thanks for taking on this code test. The requirements are listed below:

1. Create a "Foods" class or constructor that will take two arguements: a root element and a data object (foodData).
2. Render all of the items in the data object into the DOM with the root element as the parent
3. If the user clicks a food item, it should be removed from the list

Rules:
- Only vanilla JS
- Feel free to use Google, Bing, DuckDuckGo to look things up
- Time limit: 30 minutes
*/



/* DO NOT MODIFY */
const rootElement = document.querySelector('.foods');

const foodData = [
{
id: 1,
image: '🌮',
name: 'taco'
},
{
id: 2,
image: '🍔',
name: 'burger'
},
{
id: 3,
image: '🍆',
name: 'eggplant'
},
{
id: 4,
image: '🍎',
name: 'apple'
},
{
id: 5,
image: '🥞',
name: 'pancakes'
},
];
/* DO NOT MODIFY */


/** YOUR CODE BELOW **/
class Foods {
constructor(root, data) {
this.root = root;
this.data = data;
}

render() {
const documentFragment = document.createDocumentFragment();
this.root.addEventListener('click', (e) => e.target.remove());

this.data.forEach(item => {
const li = document.createElement('li');
const content = document.createTextNode(`${item.image} ${item.name}`);
li.appendChild(content);

documentFragment.append(li);
});

this.root.append(documentFragment)
}
}

const foods = new Foods(rootElement, foodData);
foods.render();

White board test

If you arrived to this, you've done a good job, even if you bloated.

Advices:

  • Ask questions on a phone screen (live coding)
    • Can I do this?
  • Talk out your solution, specially if you get stucked
  • Are they helpful?
  • Ask questions like:
    • How log you've been on the company?
    • What is your team? and what do you do in a regular day?

Preparation

  • Practive writing code without a computer with simple or general sample problems
  • Ask your friends to test you
  • Try to ask what style of tech questions will be
    • High level component design
    • Algorythm questions? binary search?
    • Regular js problems (js foundations)

White board test examples

Big-O

The longest amount of time any function is going to take giving the worst case scenario of input. Its a way of defining the the operational complexity of your program/fn.

  • Big Omega -> best case scenario O(1)
  • Big Theta -> average case O(n)
  • Big O -> worst case O(n)

Given this fn:

function search(haystack, needle) {
haystack.forEach(i) {
if (i === needle) {
return true;
}
}

return false;
}
  • Big Omega will be:
search([0,1,2,3], 0)
  • Big Theta will be:
search([0,1,2,3], 2)
  • Big O will be: (iteration thru the entire dataset)
search([0,1,2,3], 3)

There are many types of Big-O:

  • O(n!): Factorial Big-O
  • O(n): It is literally a for-loop
  • O(log n): binary tree, pretty fast
  • O(1): that's the fastest

So this example is O(n^3), because is a for loop within another, within another:

arr.map(a => {
arr.map(b => {
arr.filter(c, () => {
// ...
})
})
})

Strings

  • They are a primitive type, but with a lot of methods attatched on it.
  • Strings are immutable, if you manipulate a lot of strings, your'e creating a lot of strings. Better turn string into array .split() and then turn it back into a new string.

Remove duplicates

My solution:

function removeDuplicates(str) {
return str
.split(' ')
.reduce((acc, iteree) => {
if (acc.includes(iteree) > 0) {
return acc;
}

acc.push(iteree);
return acc;
}, [])
.join(' ')
}

removeDuplicates('This is is a test test string');

Jem's solution:

  • Sets are collections of unique values, they cannot store more than once the same value.
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
function removeDuplicates(str) {
const arr = str.split(' ');

const set = new Set(arr);
const newString = [...set].join(' ');

return newString;
}

removeDuplicates('This is is a test test string');

Flatten array (Without useing Array.flat())

My solution:

function flatten(array, initialValue = []) {
return array.reduce((acc, item) => {
if (!Array.isArray(item)) {
acc.push(item);
return acc;
}

return flatten(item, acc);
}, initialValue)
}

flatten([1, 2, [3, 4, [5, 6, 7], 8], 9, 10]);

Jem's solution:

  • He uses recursion as I do, there's the only solution to do that I think.
  • He uses Array.concat() in its approach which in my opinion is a better solution due to its simplicity.
function flatten(arr) {
arr.reduce((acc, item) => {
if () {
acc = acc.concat(flatten(item));
} else {
acc.push(item)
}

return acc;
}, []);

return someNewArray;
}

flatten([1, 2, [3, 4, [5, 6, 7], 8], 9, 10]);

Implementing Function.prototype.bind()

const foo = function() {
console.log(this.bar);
}

let baz = foo.bind({ bar: 'hello' });

baz(); // Hello

My solution: (which is wrong)

Function.prototype.bind = function(arg) {
return this.apply(arg);
}

Jem's solution:

  • I missed to just return another function calling the .call()
Function.prototype.bind = function(arg) {
const fn = this;

return function() {
fn.call(context)
}
}

Annotation: we could just do the same as Jem, but without needing to bound the fn inside the anonymous function by using a fat arrow function like this.

Function.prototype.bind = function(arg) {
return () => this.call(context);
}

Timing problem: implement debounce

const debouncedFn = debounce(console.log, 1000);

debouncedFn('a');
debouncedFn('ab');
debouncedFn('abc'); // abc (after 500ms)

My solution: (which is wrong)

function debounce(fn, delay) {
let lastTimer;

return (...args) => {
clearTimeout(lastTimer)
lastTimer = setTimeout(fn.bind(null, ...args), delay);
}
}

Jem's solution:

function debounce(fn, time) {
let setTimeoutId;

return function() {
if (setTimeoutId) {
clearTimeout(setTimeoutId)
}

setTimeoutId = setTimeout(() => {
fn.apply(this, arguments);
setTimeoutId = null; // he's garbage collecting the last setTimeoutId
}, time)
}
}

Annotations:

  • Debounce: don't execute anything within the first time until the time from the first execution to the max time hasn;t finished yet.
  • Throttle: don't execute anything else that comes after the first throttle until the first timer is not done.

Trees: basic tree

All trees have two props:

  • Root
  • Nodes
    • Children
    • Parent
// let's say we have this tree:

const tree = [
[1, 2],
[[3, 4]]
]

My solution: (which is wrong)

Jem's solution:

Rendering: move an element

  • Using requestAnimationFrame

My solution:

I didn't got the whole concept of the assignment so I couldn't continue with the exercise. 

Jem's solution:

function moveElement(duration, distance, element) {
const start = Date.now();

function move(currentTime) {
const elapsed = currentTime = start;
const progress = elapsed / duration;
const amountToMove = progress * distance;

if (amountToMove < distance) {
requestAnimationFrame(move)
}

requestAnimationFrame(move)
}
}

Promises

async function run() {
await sleep(500);
console.log('hello');
await sleep(500);
console.log('world');
}

My solution:

async function sleep(time) {
return new Promise((resolve) => {
setTimeout(resolve, time);
});
}

Jem's solution:

async function sleep(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, time);
});
}

Promisify a fn

const fn = promisify()

My solution:

async function sleep(time) {
return new Promise((resolve) => {
setTimeout(resolve, time);
});
}