Mark Thomas Miller's logo

Acing the JavaScript interview

January 1, 2020

Here's a plain-English guide on the questions you might be asked in a JavaScript, React, or web development interview. I've kept it casual so you can read at your leisure.

Tell us about yourself

At the beginning of any interview, there will probably be some interpersonal questions. Here are some of the ones I've been asked:

  • Why did you apply to this company?
  • Tell me about a project you've worked on – something you're proud of or found especially interesting.
  • What's the most recent language you learned? What did you like about it?
  • What do you want to learn next?
  • Tell me about a time you solved a big problem.
  • Tell me about a time you dealt with a difficult request.
  • Tell me about a time you dealt with a difficult team member.
  • What are your plans for your career?
  • What's your biggest weakness?
  • What's your biggest strength?
  • What's something you appreciate about JavaScript/React?
  • Do you have any more questions for us?

General JavaScript

Explain event delegation.

Event delegation is an event handling pattern that allows you to decouple creating elements from binding their event handlers.

Here's a really simple example: you have a list of items. When you click an item, you want to cross it off.

You could set an event handler for every item, but that would be pretty tedious once you had more than a few items, and you'd have to remember to rebind the onclick event handler any time you added a new item to the list.

Alternatively, you could just use event delegation to make JavaScript handle it for you. You'd set the handler to the parent element (<ul>) and your code would analyze which <li> you clicked to make it happen.

<ul id="list">
  <li>Item</li>
  <li>Item</li>
  <li>Item</li>
</ul>
const list = document.getElementById("list")
list.addEventListener("click", e => {
  // "e.target" is the clicked element
})

Aside from just being a cleaner way to write code, it also saves memory because you don't have as many event listeners on the page.

What is recursion, and how do you use it?

A recursive function calls itself until a condition is met.

Let's go over a recursive function called walkTheDOM by Douglas Crockford. walkTheDOM iterates over every element in order so you can perform a function against each of them.

For our first example, let's just push every element to an array. For instance, if we had the following structure:

<body>
  <ul>
    <li>...</li>
    <li>...</li>
  </ul>
  <div>
    <div>...</div>
    <div>
      <ul>
        <li>...</li>
        <li>...</li>
        <li>...</li>
      </ul>
    </div>
  </div>
</body>

We'd want to get an array like body, ul, li, li, div, etc.

Here's a version without any comments. Below, I'll give a more detailed explanation of how this works.

const results = [];
walkTheDOM(document.body, node => results.push(node));
console.log(results);

function walkTheDOM(node, func) {
  func(node);

  let child = node.firstChild;

  while (child) {
    walkTheDOM(child, func);
    child = child.nextSibling;
  }
}

This is what's going on:

// Create an array to hold the elements (our "results")
var results = [];

// Call walkTheDOM for the first time.
// The first argument is the element it should start from.
// The second is the function we run against each iteration.
walkTheDOM(
  document.body,
  node => results.push(node)
);

// Display the results we've collected
console.log(results);

// Again, "node" is the element we start from.
// "func" is the function we run against each iteration.
function walkTheDOM(node, func) {
  // Run the function we passed in against the node –
  // in this example, "node => results.push(node)".
  // For example, the first time this runs, it will run
  // "results.push(document.body)".
  func(node);

  // Now let's get the first child of this node.
  let child = node.firstChild;

  // We need to continue climbing all the way down the tree
  // so we can get deeply nested elements. To do this, we'll
  // use a "while" loop. While a child exists, we need to
  // run walkTheDOM on *it*, and if *that* node has a child,
  // we need to run walkTheDOM on *it*, and so on. When there
  // are no more children, we've reached the end of the document.
  while (child) {
    // Run walkTheDOM on the child node. The act of walkTheDOM
    // calling itself is called "recursion".
    walkTheDOM(child, func);

    // Now we need to see if the node has any siblings. If it
    // does, we will update "child" so the "while" loop will
    // run walkTheDOM against *it*. If it doesn't, the "while"
    // loop will end.
    child = child.nextSibling;
  }
}

Let's use a real-world example: this post. This is a pretty long post, so I want to add a table of contents to the top that lets you jump to any question. However, this is a tedious process to handle manually, so let's automate it with JavaScript.

Each section is an <h2>, and each question is an <h3> with a corresponding id. I write my posts with Markdown, so we need to use walkTheDOM to generate something like this:

**Section title**
- [Question 1](#question-1-id)
- [Question 2](#question-2-id)
- [Question 3](#question-3-id)

We can simply plug this into walkTheDOM to generate the Markdown we need:

// Same walkTheDOM function from before
function walkTheDOM(node, func) {
  func(node);

  let child = node.firstChild;

  while (child) {
    walkTheDOM(child, func);
    child = child.nextSibling;
  }
}

// Create an empty string. We'll add our results here as they come in.
let results = '';

// This is the <article> element that contains the post.
const post = document.getElementsByTagName("article")[0];

// Look at every element in the article. Make H2s (section titles) into
// bold headings, and save H3s (each question) as linked list items.
walkTheDOM(post, node => {
  if (node.nodeName === "H2") {
    results += `\n**[${node.innerText}](#${node.id})**\n`;
  } else if (node.nodeName === "H3") {
    results += `- [${node.innerText}](#${node.id})\n`;
  }
});

// Show the results:
console.log(results);

This actually works – run it in your console to try it out!

For another example of recursion, take a look on my post at flattening multidimensional JavaScript arrays.

Write a FizzBuzz program.

A "FizzBuzz" question is sometimes used to determine if you are comfortable with JavaScript's for and if statements. The typical assignment goes like this:

Write a program that prints the numbers from 1 to 100. For multiples of three print "Fizz" instead of the number, and for multiples of five print "Buzz" instead of the number. For numbers which are multiples of both three and five print "FizzBuzz".

There are many ways to solve this, but this is my approach:

for (let i = 1; i <= 100; i++) {
	let output = '';
	if (i % 3 === 0) output += 'Fizz';
	if (i % 5 === 0) output += 'Buzz';
	console.log(output || i);
}

What’s a closure?

A closure is a fancy term for using the lexical scope to keep variables private. Of course, this doesn't mean anything if you don't first understand what lexical scoping is, so let's cover that first. It simply means that a variable can only be referenced from within the block of code it was defined:

function example () {
  var a = 1
}

console.log(a) // Uncaught ReferenceError: a is not defined

A closure can use lexical scoping to keep your program's variables private. Here's an example: let's say we have a variable called privateCounter, and we don't want anyone to be able to access it directly. Instead, we want them to call one of our own defined functions to interact with it. We'll let them either increment, decrement, or see its value – but nothing else. We can achieve this with a closure:

// Here, we assign an immediately-invoked function to `counter`.
var counter = (function() {

  // privateCounter and changeBy are private. They can't be directly
  // accessed from outside of counter.
  var privateCounter = 0;

  function changeBy(val) {
    privateCounter += val;
  }

  // Public! We can call counter.increment(), counter.decrement(), and
  // counter.value() to interact with privateCounter.
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  };
})();

console.log(counter.value()); // 0
counter.increment();
counter.increment();
console.log(counter.value()); // 2
counter.decrement();
console.log(counter.value()); // 1

So what's actually happening here? You can think of a closure as creating its own little "world" when you create an instance of it. For instance, let's create two instances of outer() via a and b.

function outer() {
  var i = 0
  return function inner() {
    return i++
  }
}

// Here, we create two instances of our outer() function.
// That means that there'll be two separate instances of `i`!
var a = outer()
var b = outer()

// If we run a() a few times, a's version of i will count upward.
console.log(a()) // 0
console.log(a()) // 1
console.log(a()) // 2

// If we run b(), we can see that its version of i wasn't impacted.
console.log(b()) // 0

What does this refer to in JavaScript?

this has different values depending on how it's used, but a simple way to think about it is that this refers to the object that called the function.

W3Schools has a nice breakdown of how this works:

  • methods: this is the owner object
  • alone: this is the global object
  • function: this is the global object
  • function, strict mode: this is undefined
  • event: this is the element that received the event
  • call, bind, and apply can attach this to different objects

What's the difference between == and ===?

=== performs a strict comparison to see if both the values and the type are the same.

== checks if two values are the same, but doesn't check against their types.

1 === "1" // false; values are the same, but a string is not a number
1 == "1" // true; values are the same

What is an anonymous function, and when would you use one?

If you plan on using a function in more than one place, don't make it anonymous. Otherwise, an anonymous function can be a nice option for two main reasons: first, they don't pollute the namespace. And second, they make your code more succinct and readable — that is, as long as you're not nesting a lot of them inside each other.

What are some common HTTP request methods, and how are they used?

Some of the most common HTTP request methods are GET, POST, PUT, and DELETE.

  • GET requests data – for instance, retrieving a JSON file so you can use its data to populate your UI.
  • POST sends data – for instance, submitting a form to create a new blog post.
  • PUT updates data – for instance, updating an existing blog post with data you send.
  • DELETE deletes data – for instance, deleting a blog post.

If these concepts are confusing to you, you might want to read my post on backend development for frontend developers.

What are some common ways to increase performance?

There are a number of common strategies that I've used to increase the performance of webpages and applications.

  • Debounce/throttle computationally intensive functions. (You can learn more about debouncing elsewhere in this guide.)
  • Implement server-side rendering.
  • For single-page applications, take advantage of code splitting.
  • Minify/gzip code.
  • Minify, resize, and format images correctly. For instance, you might be able to use SVGs instead of PNG icons; your background image could be resized and minified to fit a user's specific viewport size (one for mobile, one for desktop), etc.
  • Strip your page of unnecessary/unneeded resources. (Perform an audit of font families, font weights, tracking scripts, unnecessary images, etc.)
  • Use Lighthouse to measure performance and follow its suggested guidelines.
  • When possible, wait for the initial page render to complete before executing scripts. (<script defer>)
  • Use a CDN.
  • Split your site into pieces. For instance, you may not need to hit your database and load your billing scripts and in-app tracking scripts on your marketing site; therefore, you could use something like Jekyll or Gatsby to make lightweight/cost-effective landing pages and run your actual product on a subdomain such as app.example.com.

How does "require" differ from "import"?

require is the import keyword of the CommonJS module system. It got popular because Node uses it.

var React = require('react')

ES6 introduced import. import lets us take advantage of Webpack's tree shaking as well as asynchronous loading.

import React from 'react'

What's a pure function, and when would you use one?

Pure functions form the basis of functional programming and they're easy to move, refactor, etc. Given the same inputs, they will always return the same output, and they don't produce any side effects. A really simple pure function would be something like:

function square(i) {
  return i * i
}

What are the pros/cons of using functional iteration compared to a "for" loop?

It could be argued that functional iteration (map, reduce, etc.) produces cleaner, more readable code. It's also a functional style of programming, so data structures are immutable and likely easier to debug.

arr.map(x => {
  // Each element is accessible as `x`
})

However, a for loop can still be a good choice at times mainly due to the performance benefits.

for (var i = 0; i < arr.length; i++) {
  // Each element is accessible as `arr[i]`
}

What's a prototype?

In JavaScript, every object has properties. Objects have prototypes which are basically fallbacks, or default values, for these properties.

Let's say you have an object called person. If you try to call person.speak() and it's undefined, it will look to that object's prototype for a .speak() method, and follow the prototype chain all the way to the first prototype to try to resolve it.

What's the difference between a function and a method?

A method is a function that's part of a Class or an Object.

// Function
function foo() {
  // ...
}

class Example {
  // Method
  bar() {
    // ...
  }
}

const SecondExample = {
  // Method
  baz: function() {
    // ...
  }
}

What are the different ways to define a function?

There are a few different ways to define a function:

function exampleName () {
  // vanilla function definition
}

const exampleName = function() {
  // named function expression
}

const exampleName = () => (
  // ES6 arrow function
)

What are the benefits of ES6 arrow functions?

Arrow functions are a new way to write functions in ES6. They provide a number of benefits:

  • They automatically bind this to the surrounding context.
  • They use an implicit return when there's no body block (that is, {}).
  • They use a compact syntax that enables you to write less verbose code.
// Regular function
arr.map(function(item) {
  return item.id
})

// Arrow function
arr.map(item => item.id)

Arrow functions remove a lot of boilerplate in React because they remove the need to bind this inside the constructor:

class Example extends Component {
  state = {
    text: "Click me!"
  }

  handleClick = () => {
    this.setState({
      text: "I've been clicked!"
    })
  }

  render () {
    return (
      <button onClick={ this.handleClick }>
        { this.state.text }
      </button>
    )
  }

}

Explain functional methods for arrays in JavaScript (map, filter, reduce).

There are several function methods for arrays in JavaScript:

  • map creates a new array by calling a function on every item in an array.
  • filter creates a new array with any elements of another array that pass your defined test.
  • reduce applies a function against an accumulator and each element in an array to reduce it to a single value. It's the most complicated of these methods, but it's very versatile. (For instance, Redux is basically a reducer!) Here's an example of adding together an array with a reducer:
[0, 1, 2, 3].reduce((acc, current) => (
  acc + current
))

// what's happening here:
// 0 + 1 = 1
// 1 + 2 = 3
// 3 + 3 = 6
// returns 6

What's the difference between .call, .bind, and .apply?

They can all be used to set the context of a function. You may know context as the value of this.

  • bind creates a new function that has its this keyword set to the provided value. Later, you can call the function you created with bind any time you want.
  • call immediately executes a function with the this keyword set to the provided value. doesn't create a new copy of the function.
  • apply is like call, except it accepts an array of values.

Here's a useful tip for React developers. You can get around binding this inside your class components by using arrow functions.

// Before
class OldWay extends Component {
  constructor(props) {
    super(props)
    this.handleClick = this.handleClick.bind(this)
  }

  handleClick() {
    // ...
  }

  render() {
    return (
      <button onClick={this.handleClick}>Click me!</button>
    )
  }
}

// After
class NewWay extends Component {
  handleClick = () => {
    // ...
  }

  render() {
    return (
      <button onClick={this.handleClick}>Click me!</button>
    )
  }
}

What is a debounced/throttled function?

Debouncing is a performance optimization you can do to limit the rate at which a function can fire. For instance, if you have a computationally-intensive function that should only be running every 500 milliseconds, you could debounce it:

const debouncedFunction = debounce(() => (
  // ...
), 500)

You could implement debounce on onScroll, resize, etc.

What is stringify?

For many operations, such as sending data to a server, you'll need to convert your existing objects or arrays to a string. You can convert them with JSON.stringify(yourObj).

What is AJAX?

AJAX is a core feature of JavaScript that lets you update pages without reloading them – like clicking the "Like" button on Facebook or upvoting something on Reddit (although Facebook might be using web sockets for Like buttons now – I'm not sure).

What is WebSockets?

WebSockets provides a connection between the browser and the server that allow for two-way data flow with games, media, etc. For instance, you could use WebSockets to make a chat app!

React

Explain, at a high level, how React works.

React works as the "view" layer of your application. In other words, the UI.

It allows you to break your UI into modular components: buttons, headings, inputs, media galleries, and so on. In one of my projects, I even created a custom <Div /> component so I could swap out common divs with a custom HTML element. Good components are reusable across many different contexts, and once they're set up, assembling a new UI becomes easy.

React also handles state changes. When anything in your application changes state (say, you open a menu), React renders a new element tree inside the virtual DOM. Then, it compares the new tree against the old tree and updates only the necessary parts of the UI. This makes React applications feel snappy/responsive.

React allows you to pass "props" into your components. Props are just variables that you can incorporate into your components – for instance:

<MediaGallery title="Your Pictures" images={ fileArray } />

In that example, title and images are props. You can pass anything through props – strings, arrays, even other components! And for that example, we could map through each of the images inside the MediaGallery to display them.

Finally, React enforces one-way data binding. In other words, props are passed from parents to children – not the other way around.

Where should you make AJAX requests in React?

At first glance, the only two logical places to put an AJAX request are componentDidMount and componentWillMount. But componentWillMount turns out to be a bad idea because the request may complete before the component mounts, and you’ll be stuck trying to set the state on a component that hasn’t mounted. It’s like trying to eat a grape that doesn’t exist yet.

So, place your AJAX requests in componentDidMount.

What are the best practices for using setState?

A lesser-known fact about setState is that it's asynchronous. This means that if you call setState and immediately afterwards check against state, it probably won't be updated yet.

For that reason, the best way to run code after setState is to use a callback function:

enable() {
  this.setState({ enabled: true }, () => {
    console.log(this.state.enabled);
  })
}

Similarly, if you're trying to calculate a new state based on a property in the old one, you should pass in the old state:

// Potentially hazardous
toggle() {
  this.setState({ show: !this.state.show })
}

// Correct
toggle() {
  this.setState((prevState, props) => ({
    showForm: !prevState.showForm
  })
}

What are the best practices for adding event listeners to components?

If you need to add an event listener to a component, you should set it in componentDidMount. Then, remember to remove it in componentWillUnmount to prevent memory leaks.

Explain, at a high level, how Redux works.

Once your application gets large enough, state management can become a pain. Enter Redux, a way to manage state inside a larger React application, based on the concept of reducers (explained in the next question). This is how it works:

  1. You store your application's state in one place – the store.
  2. Components hook into the store to derive their state.
  3. When an action occurs, we use a reducer to return a new state.
  4. The new state becomes active in the store and is pushed to your application.

It's important to note that we never mutate the state – we just use a reducer to create a new one. The store acts as our single point of truth for state across the whole application.

I wrote a simple guide to setting up Redux here.

How does a reducer work?

I think that people have trouble understanding reducers because of their name. In React, we shouldn't think about reducers reducing things in the normal sense of the word ("to make smaller or less in amount, degree, or size") — instead, they transform things into a new version.

In React, a reducer function takes two arguments: 1. the current state and 2. an action. Robin Wieruch's post explains this beautifully:

const counterReducer = (count, action) => {
  switch (action.type) {
    case "INCREASE":
      return count + 1;
    case "DECREASE":
      return count - 1;
    default:
      return count;
  }
}

This reducer could be used like counterReducer(0, "INCREASE") and would produce 1. And in a real application, it would usually accept a state object instead of a single count value:

const counterReducer = (state, action) => {
  switch (action.type) {
    case "INCREASE":
      return { ...state, count: state.count + 1 };
    case "DECREASE":
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
}

If you take a close look at this function, you'll realize something: reducer functions don't directly the state you pass in — instead, they return a new object. This is what people mean when they say that you "don't mutate state".

When would you use stateless functional components vs. class components vs. pure components?

If you need state or lifecycle methods, use a class. Otherwise, use a functional component. If performance is a concern, use a pure component to limit the number of unnecessary re-renders that occur.

// Class component
class Header extends Component {
  render() {
    return <Menu items={this.props.items} />
  }
}

// Functional component
const Header = ({ items }) =>
  <Menu items={items} />

// Pure component, option 1
class Option1 extends PureComponent {
  render() {
    return <Menu items={this.props.items} />
  }
}

// Pure component, option 2
class Option2 extends Component {
  shouldComponentUpdate() {
    // define custom logic here...
  }

  render() {
    return <Menu items={this.props.items} />
  }
}

If you're using hooks, you can still have state inside a functional component. More on that in a moment.

Describe the React component lifecycle.

The React component lifecycle happens in the following order. #5 is a little more complicated, so I'll go into specifics below:

  1. The component receives its props and state.
  2. componentWillMount
  3. renderthe component is added to the DOM
  4. componentDidMountgreat place to fetch data, add event listeners...
  5. The component waits for new props or state. — see below
  6. componentWillUnmount happens before the component is removed — great place to clear intervals, remove event listeners, and do any other work to prevent memory leaks before a component leaves the DOM

To talk about #5 in more detail, this is the lifecycle that occurs when updating props or state:

  • componentWillReceiveProps occurs if the props are updated
  • shouldComponentUpdate checks if the component should update (you can add logic here for custom-rendered pure components)
  • componentWillUpdate fires
  • render fires again
  • componentDidUpdate fires

How do you use React Hooks?

React's hooks mechanism lets you do more things with function components. Hooks remove a lot of the boilerplate of using class components to handle state.

  • useState lets you give state to a functional component.
  • useEffect lets you run code when a variable updates.
import React, { useState, useEffect } from 'react'

function Counter(props) {
  // `count` is the state variable.
  // `setCount` is the function that updates our state variable.
  const [count, setCount] = useState(0)

  // `useEffect` can run code every time our state variable is updated.
  useEffect(() => {
    console.log(count)
  }, [count])

  // Pass the new value of `count` into `setCount`
  function increment() {
    setCount(count + 1)
  }

  return <button onClick={increment}>{count}</button>
}

What are some things you like about React?

I've been asked this by at least three different interviewers. Here are things I legitimately like about it:

  • React is in a position that allows developers to take full advantage of the JavaScript ecosystem (via npm). Packages exist for common technical challenges like data visualization, text editing, etc. so it's often just a job of gluing together different components. In that way, it's almost like gems in Ruby on Rails.
  • React encourages you to break your application into tiny components. When this infrastructure is well-designed, assembling a UI becomes fast, elegant, and almost effortless. I love this feeling.
  • It has one of the best technical communities around – even designers are able to participate thanks to great documentation and an inclusive community.
  • ES6+ is nice to write.
  • Once you understand the component lifecycle and functional stateless components, React becomes almost as easy as writing plain HTML with some logic added in.
  • React's one-way data binding enforces a certain structure and organization to your application that you can't find in some other frameworks. This also helps prevent bugs.
  • React is extremely stable – even the alphas. With such a large community and a talented core team, the typical developer won't run into bugs with the framework itself.

Why does React use a virtual DOM?

The virtual DOM is basically a recursive structure of nested objects. React uses it to determine if it should re-render your UI. When something changes in your application – say, a state is updated – React will compare the old tree of objects with the new one to determine if there are any differences. If so, it updates the UI accordingly.

Explain the standard toolchain for React development.

To develop with React, you'll usually need some combination of the following:

  • Dependency management with tools like npm or yarn – in the same way that you'd use gems in Rails or plugins in WordPress. For instance, you can add a package for a React-based color picker component so you don't need to write it yourself.
  • A compiler such as Babel that takes the ES6/JSX/CSS code that you write and transpiles it into ES5, regular CSS, etc. The act of compiling down to ES5 is called transpilation.
  • When your files are built for production, they should usually be minified and compressed so their filesize is smaller for the end user. Optionally, you can generate source maps that give you context for errors in production.

What some of the benefits of React Hooks?

React Hooks remove some of the pains of working with classes:

  • You no longer need to worry about binding this.
  • You no longer need to nest components ("wrapper hell") to get certain functionality.
  • You can write less code to do the same thing.
  • Classes are harder for the compiler to optimize; functions are much easier.

What is a higher order component (HOC)?

Higher order components accept other components as children. With this in mind, an easy way to spot an HOC is that it uses this.props.children in the render method. HOCs let you add functionality to other components by simply wrapping them. For instance, if you have a higher order component called withLogging, you could wrap a component in it to give it access to logging: withLogging(ExampleComponent). HOCs are a good choice when you want to add some behavior to lots of different components in your app, and this behavior doesn't need to be customized for each individual component. Components should be able to stand on their own without being wrapped by an HOC. You can also use them for hijacking renders and prop manipulation.

What are synthetic events in React?

React wraps native browser events in a SyntheticEvent wrapper to make sure events work identically across browsers. If needed, you can retrieve the underlying browser event with nativeEvent.

Should you use mixins?

React's documentation previously recommended mixins as a way to handle cross-cutting concerns (for instance, sharing the same layout or authorization between all of your components). However, they're now against it and recommend using higher order components (HOCs) to handle this instead.

How do keys work in React?

When you're rendering a list of things (such as mapping through an array), you can use keys to help React identify which items changed. Keys provide a stable identity for these items.

How do refs work in React?

React's refs let you modify an element from outside of the typical data flow. The general rule of thumb is not to use them, although you can in the following cases:

  • Managing focus, text selection, or media playback.
  • Triggering imperative animations.
  • Integrating with third-party DOM libraries.

What is prop drilling? (Should you do it?)

Prop drilling is basically passing props through multiple levels of components so they can be accessed by a descendant. For instance:

GreatGrandparent # has state item called "foo"
	Grandparent
		Parent
			Child
				Grandchild # needs "foo"

If Grandchild needs foo, you could pass it from GreatGrandparent to Grandparent, then from Grandparent to Parent, then from Parent to Child, and finally from Child to Grandchild... but that's pretty messy. Passing props down multiple levels is referred to as prop drilling, and it's generally not recommended. Alternatively, you could use React's Context API or something like Redux or MobX. Of all of these options, I think Context is the simplest/cleanest.

What is Redux Thunk?

To understand Redux Thunk, you should first understand the concept of Redux's actions: actions are simply objects that contain a type and some other code that you can push into a new state.

{
  type: "INCREASE",
  count: 1
}

As Dave Ceddia puts it...

It is a middleware that looks at every action that passes through the system, and if it's a function, it calls that function. That's all it does.

The benefit of Redux Thunk is that you can dispatch a function as an action rather than just a plain object (like our INCREASE example above).

Why do methods need to be bound to a class? Can you avoid the need for binding?

When working with classes, you probably expect this to point to the instance of the class you're calling it from. Usually, you'd need to bind this in the constructor; however, you can get around this by using arrow functions:

// Instead of this:
class Example extends Component {
	constructor(props) {
    super(props)
    this.state = {
    	x: false
    }
    this.doSomething = this.doSomething.bind(this)
  }

	doSomething() {
		this.setState({ x: true })
	}

	render() {
		return (
			<button onClick={this.doSomething}>Run</button>
		)
	}
}

// You could use this:
class Example extends Component {
	state = {
		x: false
	}

	doSomething = () => {
		this.setState({ x: true })
	}

	render() {
		return (
			<button onClick={this.doSomething}>Run</button>
		)
	}
}

If you want to avoid this entirely, you can use a hook.

What is the StrictMode component and why would you use it?

You can wrap components in the StrictMode component to highlight potential problems in your application. It activates additional checks and warnings on its descendants. This helps you deal with identifying components with unsafe lifecycles, detecting legacy APIs, and detecting unexpected side effects.

What is shallow rendering? What are its pros and cons?

Shallow rendering is used when testing components in React. It lets you render a component one level deep and assert facts about what its render method returns, without worrying about the behavior of its children components. Shallow rendering is fast and only relies on the component you're testing. However, it won't catch errors in child components, so if you're using them, you'll want to make sure to test the children too. Some people, such as Kent C. Dodds (the author of React Testing Library) never use shallow rendering:

With shallow rendering, I can refactor my component's implementation and my tests break. With shallow rendering, I can break my application and my tests say everything's working. [...] it lulls you into a false sense of security. The reason I write tests is to be confident that my application works and there are far better ways to do that than shallow rendering.

What are React testing best practices?

The closer you get to simultating a user experience, the more confident you can be that your tests have you covered. Again, Kent has a great example on this: instead of testing your implementation...

const usernameInput = wrapper.find("input").first();
const passwordInput = wrapper.find("input").last();

...you should remember that users will look for your fields by the label name and implement your tests from there.

const usernameInput = getByLabelText("Username");
const passwordInput = getByLabelText("Password");

Or for another example, instead of looking that an element has a className of button, you should make sure that it's clickable.

Write tests so they rarely need to change when you refactor code. Don't use shallow rendering. Instead of using id attributes to find elements when testing, use data-test attributes.

A general rule of thumb is that if statements are great places to test.

How can you perform an action only once in a functional component?

In functional components, you can perform an action once by calling useEffect and passing in an empty array.

import React, { useEffect } from 'react'

const Example = () => {
	useEffect(
    () => { runThisOnce() },
    []
  )

	return ...
}

The second param of useEffect can be several different values:

  • undefined, which will run useEffect on every render
  • an array of variables, which will run useEffect any time they're updated
  • an empty array, which will run useEffect on the first render; similar to componentDidMount

How would you investigate and fix a performance issue in React?

If your React app is running slowly, there could be a number of issues – maybe a component that is re-rendering too often, a state is updating too often, or something similar. To detect what's happening, you can download the React Developer Tools browser extension and use its Profiler tool.

If the issue is with component re-rendering, you can prevent unnecessary re-renders by using React.memo() for functional components and PureComponent (or shouldComponentUpdate()) for class components.

If the issue is with context (maybe a certain value in a provider is updating too often, which needlessly updates components listening to that context), you may need to split your context into separate parts.

HTML & CSS

Why do browsers read CSS from right to left?

When a page loads, the browser has to comb through every element and decide what styles it needs. For instance, if it finds a menu item with the class cta, then it needs to comb through all of your CSS to find out how to style cta.

The browser wants to do this as fast as possible, so it reads selectors from right to left. You read that correctly – because the selector on the right needs to match the element it's testing for. Here, the browser would first look for .cta, completely ignoring .menu.

.menu .cta {
  color: blanchedalmond;
}

If that's the only rule with .cta, it's finished! If there are multiple .cta classes, though, now the browser knows to go through their trees, still from right to left.

.post .cta {
  color: navajowhite;
}

.menu .cta {
  color: blanchedalmond;
}

What are some common practices to help accessibility?

Here are a few off the top of my head:

  • text should have sufficient color contrast and be a readable size
  • make clickable elements look like they're clickable, and give some visual indication of their interactivity on hover
  • use semantic HTML (for instance, if you need to make a button, use <button> instead of <div>)
  • use aria-label to label elements (for instance, "Previous Slide/Next Slide" buttons on a carousel); MageTools makes checking this easier
  • label your inputs (HTML placeholders are not labels)
  • from a copywriting perspective, use clear/simple language to label links
  • allow keyboard-only usage of your website (GitHub does this well with a "Skip to Content" button when you press Tab)
  • don't overwrite the highlight that appears on focused elements (or, if you must overwrite it, make sure your alternative is highly visible and supported across browsers)
  • remember that most users aren't using an expensive, max-brightness retina display with a fiber ethernet connection; make UIs that work for your average user, on the average device, on the average Internet speed
  • add descriptive alt text to images
  • make your application responsive if it's used in that context

Explain flexbox. How do you use it? What are the benefits?

Flexbox stands for "flexible box". It's a way to align items even if you don't know their exact size.

Flexbox lets us create complex interfaces with just a tiny bit of code. For instance, let's say we wanted to make a navigation header with a logo on the left and a menu on the right. Here's how we'd do that with flexbox:

<nav>
  <div>Logo</div>
  <div>Menu</div>
</nav>
nav {
  display: flex;
  align-items: center; /* Vertical positioning */
  justify-content: space-between; /* Horizontal positioning */
}

In that example, the logo would be positioned on the left and the menu would be positioned on the right. They're also vertically aligned to the center, so if the logo had a higher height than the menu, the menu would still be aligned to the center of the logo.

With flexbox, you can also align items in the dead center of the screen (great for popups and such):

.center-of-screen {
  display: flex;
  align-items: center;
  justify-content: center;

  width: 100%;
  min-height: 100vh;
}

There are two display values for flexbox-type elements: flex (most similar to block) and inline-flex (most similar to inline-block). Blocks are fullwidth; inline blocks only take up as much width as they need to.

The immediate children of a flex or inline-flex element can adjust their sizing with the flex property:

.flexy {
  flex: flex-grow flex-shrink flex-basis;
}
  • flex-growHow much the element will grow compared to its siblings.
  • flex-shrinkHow much the element will shrink compared to its siblings.
  • flex-basisThe size of the element.

For instance, flex: 0 0 250px; would create an element with 250px in width; flex: 1 would make an element take up as much space as possible.

By default, a parent with display: flex will squish all of its immediate children into a horizontal line, regardless of their display values. If you want to align them vertically, you can assign flex-direction: column to the parent. This will reverse align-items and justify-content, so align-items will then handle horizontal positioning and justify-content will handle vertical positioning.

Flexbox can also be used to make simple grid systems as I've shown in my other post on a ~15 line grid system, although you might be better off using the new CSS grid system.

How does box sizing work?

Without applying a box-sizing property in CSS, the size of an element is calculated as:

totalwidth = width + padding + border
totalheight = height + padding + border

The most common hint I've seen that box-sizing isn't set is a horizontal scrollbar on the bottom of a page. Sometimes people set elements to width: 100% and give them padding or border without setting box-sizing, and this causes the element to take up more space than the screen width. What good is a width property if it's not setting the total width of the element?

box-sizing: border-box allows us to think more intuitively about making layouts. It fixes the problem by including both the padding and border in the total width and height calculation. Therefore, with box-sizing: border-box:

totalwidth = width
totalheight = height

What is the "rem", or "root element", in CSS?

rem stands for root element, which is the font-size property of your HTML. You can use it in your CSS like:

html {
  font-size: 20px; /* rem */
}

h1 {
  font-size: 3rem; /* becomes 60px */
}

p {
  font-size: 1rem; /* becomes 20px */
}

Promises & asynchronous operations

What is "asynchronous" in JavaScript? What are its benefits?

Sometimes, we need our code to communicate with another server/the file system/etc. We don't know how long these operations are going to take, so we write the code to be asynchronous – meaning that it's non-blocking, so other code can continue to run while we wait for the response. Nobody enjoys spelling asynchronous so we abbreviate it as async in the code.

Explain how async calls work behind the scenes in JavaScript.

Async calls take advantage of something called an event loop. Basically, we start a request and simply listen for its completion. That way, our code can keep running without waiting for the result. When we get the signal that our request is complete, our event handler can handle the response.

What is a promise?

MDN has a wonderful guide on promises, and they describe them well:

Promises represent the eventual completion or failure of an async operation... Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function.

The key point is that we don't need to pass callbacks into a function, so this:

function success(result) {
  console.log(result)
}

function failure(error) {
  console.error(error)
}

example(data, success, failure)

Becomes this:

function success(result) {
  console.log(result)
}

function failure(error) {
  console.error(error)
}

example(data).then(success, failure)

For a more real-world example, this is what it would look like to use a promise to fetch a JSON file with fetch:

fetch('https://example.com/posts.json')
  .then((response) => response.json())
  .then((json) => console.log(json))
  .catch(error => console.error(error))

What are the benefits of JavaScript promises?

Data returned by promises is 1. immutable, 2. cleaner to read/write, and 3. helps us avoid the spaghetti code that comes with callbacks.

What are the async/await keywords, and what are their advantages and disadvantages?

You can use async/await as an alternative to writing promises. The syntax is rather clean (there are no anonymous or nested functions, as with chaining .then) and it can be cleaner for error handling.

In simple terms, write async before a function that contains await; write await before an expression that returns a promise. Like so:

async function x() {
  const y = await z()
  console.log(y)
}

function z() {
  return new Promise(resolve => {
    setTimeout(() => { resolve('Hello!') }, 1000)
  })
}

x() // Returns "Hello!" after 1000ms

Or, if you need to resolve multiple promises at the same time, use Promise.all:

function foo() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('Hey')
    }, 1000)
  })
}

function bar() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('there')
    }, 1000)
  })
}

async function example() {
  const [a, b] = await Promise.all([
    foo(),
    bar()
  ])

  console.log(`${a} ${b}`)
}

example() // Returns "Hey there" after 1000ms

Design patterns

What is the purpose of design patterns?

They're basically "proven structures" to solve common problems that programmers face every day. They help us 1. not reinvent the wheel, 2. make our code easier to maintain, and 3. make it easier for other developers to work alongside us. In real life, it'd be like making a chair: you could use the "chair design pattern" and know that you'll need a back, a seat, and four legs.

It's easy to write bad/messy/unmaintainable code, so design patterns help us make our code more maintainable and elegant.

What are some design patterns in JavaScript?

I recommend reviewing this article for more a more comprehensive look at some of the design patterns in JavaScript, because this post is getting long. However, I can give a brief overview here:

  • Creational design patterns give you better ways to create objects.
  • Structural design patterns keep your code flexible.
  • Behavioral design patterns help different parts of a system communicate with each other and stay in sync.
  • Architectural design patterns are used for application architecture (such as MVC, or Model View Controller, in Rails).

The constructor pattern allows us to create new objects, each with their own scope. In ES6:

class Person {
  constructor(first, last) {
    this.first = first
    this.last = last
  }

  getName() {
    return `${this.first} ${this.last}`
  }
}

const x = new Person("Mark", "Miller")
console.log(x.getName()) // "Mark Miller"

The revealing module pattern uses a closure to create a public/private interface for interacting with variables. This example is adapted from the module pattern on MDN (which I visit it in Closures in more detail).

var counter = (function() {

  // Private
  var privateCounter = 0;

  // Private
  function increase() {
    privateCounter++;
  }

  // Private
  function decrease() {
    privateCounter--;
  }

  // Private
  function getValue() {
    return privateCounter;
  }

  // Public
  return {
    increment: increase,
    decrement: decrease,
    value: getValue
  }

})();

console.log(counter.value()); // 0
counter.increment();
counter.increment();
console.log(counter.value()); // 2
counter.decrement();
console.log(counter.value()); // 1

The template pattern creates the skeleton of an operation and allows us implement fine-grained details inside our subclasses. For instance:

class Employee {
  constructor(name) {
    this._name = name
  }

  work() {
    return `${this._name} handles ${this.responsibilities()}`
  }
}

class Engineer extends Employee {
  constructor(name) {
    super(name)
  }

  responsibilities() {
    return 'JavaScript development'
  }
}

const engineer = new Engineer('Mark')
console.log(engineer.work()) // Mark handles JavaScript development

The facade pattern creates a simpler interface to a complex feature. The article I linked above explains it with an example from jQuery's selector feature:

Although it seems simple on the surface, there is an entire complex logic implemented under the hood in order for this to work.

jQuery(".parent .child div.span")

What are higher order functions?

Higher order functions are functions that accept other functions as arguments. For instance, map is a higher order function because you can pass a function as its first argument:

const a = [10, 20, 30, 40]
const b = a.map(myFunction)

function myFunction(num) {
  return num * 100
}

There are also some nice examples from Eloquent JavaScript:

function greaterThan(n) {
  return m => m > n;
}

let greaterThan10 = greaterThan(10);
console.log(greaterThan10(11)); // true
function repeat(n, action) {
  for (let i = 0; i < n; i++) {
    action(i);
  }
}

function unless(test, then) {
  if (!test) then();
}

repeat(3, n => {
  unless(n % 2 == 1, () => {
    console.log(n, "is even");
  });
}); // 0 is even; 2 is even

What are some patterns that you like to use in React?

I really enjoy using some of these patterns in React:

Instead of rendering plain HTML, I try to create components for specific HTML elements. For instance, if I have an H2, I tend to use <H2 {...props}>...</H2> instead of <h2>...</h2>. This has a few advantages:

  • If we decide to add special functionality to our H2s later (like automatically generating their anchor tags), then we'll only have to change it in one place.
  • If the application is going to be embedded on another site, we can change one element to make sure its styles won't be overridden. For instance, when developing MageTools, I designed <Div> components that actually render a custom HTML element called <magetools-div> so its styles won't conflict with other <div>s across sites (for context, it's a Chrome extension).

Another pattern I use is mapping arrays to generate lists. This helps keep the code much cleaner, shorter, and easier to extend:

const menuItems = [
  { slug: '/general', text: 'General Settings' },
  { slug: '/billing', text: 'Billing' },
  { slug: '/profile', text: 'Profile' }
]

const Menu = () => (
  <ul>
    {
      menuItems.map(item => (
        <li key={item.slug}>
          <Link to={item.slug}>{item.text}</Link>
        </li>
      ))
    }
  </ul>
)

And in some circumstances, I'll pass components as props:

<Preview show={<Website />} />

Finally, I like to use smart components and dumb components. Smart components hold state, run functions, and pass props, and dumb components simply accept props to render UI.

Programming Paradigms

What are the two main programming paradigms important to JavaScript?

JavaScript can be both object oriented (with prototypal inheritance) and functional. I'll explain both below.

What is OOP (object oriented programming)?

Object oriented programming lets us split our code into objects that inherit methods. For instance, in React, each class component is an object that has the same lifecycle methods: componentDidMount, render, etc.

What is functional programming?

Functional programming avoids state and mutable data.

The basis of functional programming is the pure function, which I've discussed elsewhere in this post, but I'll revisit it here: given the same inputs, a pure function will always return the same output without producing any side effects. A really simple pure function is:

function square(i) {
  return i * i
}

Notice that square doesn't need to interact with any other part of the application to do its job. You simply pass it a number, and it will give you a result. The result will be the same every time, regardless of the state of your application.

When would you use inheritance in JavaScript?

You can use inheritance when you need to add the same functionality to multiple related objects. There are two types of inheritance: from classes and from prototypes. I'll explain more below.

What's the difference between class and prototype inheritance?

JavaScript isn't class-based – it's prototype-based. When people talk about classical inheritance in JS, they're talking about keywords like class.

A JavaScript class is like a blueprint. When you create an object from a class, it gets the class's shape. But if you modify the class later, it won't change the object you created. It's smart to avoid class hierarchies in JavaScript because of this limitation.

A prototype is a full-blown object that can be linked to from another object. When something doesn't exist on the object, the runtime looks to the prototype, and then the prototype's prototype, and so on. If you modify the prototype, you also modify the behavior of all of its linked objects.

When is classical inheritance appropriate?

Generally, you'll want to use prototypal inheritance in JavaScript where possible. But if you won't need to modify the class you're inheriting from (such as creating a Component in React), you can use classical inheritance:

class Example extends Component {
  // ...
}

Functions as first class citizens

"Functions are first class citizens" means that they can be assigned to variables, passed as arguments, etc. To keep this example dead simple, here's an example of passing in a function, getMark(), as an argument:

function greet(name) {
  console.log(`Hello ${name}!`)
}

function getMyName() {
  return "Mark"
}

greet("Edelgard") // Hello Edelgard!
greet(getMyName()) // Hello Mark!

What are the pros and cons of functional vs. object oriented programming?

They're just different approaches, and neither is the "right" or "wrong" approach. OOP is very readable but depends on shareable state (which can lead to unexpected bugs when two parts of your application are messing with the same state). Functional programming is the opposite – it uses pure functions (functions that, given the same input, provide the same output every time with no side effects). Functional programs are modular and easier to refactor for performance/optimization. However, in some cases, functional programs are harder to read and understand.

What is hoisting in JavaScript?

JavaScript reads from top to bottom, but its variables are hoisted to the tops of their scopes. This lets you call functions before they're defined:

example()

function example() {
  // ...
}

What is lexical scope?

Lexical scopes make it so a variable can only be referenced from within the block of code where it's defined. (In other words, {}.) For instance, you couldn't do:

function example () {
  var a = 1
}

console.log(a) // Uncaught ReferenceError: a is not defined

What is an immediately invoked function expression (IIFE)?

An immediately invoked function is a self-executing function. Here's an example:

// Invoking a function explicitly
function invokeMe() {
  alert("Invoked!");
}
invokeMe();

// Immediately invoking a function
(function() {
  alert("Invoked!")
})();

Immediately invoked functions can be used to assign an instance of a function to a variable – say, our closure example above:

var counter = (function() {
  // ...
})();

As an even simpler example, let's say that you're working on a big application that will use a lot of different scripts. You can add your code to an IIFE to make sure it runs immediately but doesn't pollute the namespace:

// Global variables from other scripts
const name = "KEVIN";
const age = 400;

// With this IIFE, you can use the same variable names, but you
// won't see errors from trying to overwrite name and age. In
// addition, the outside environment can't read or overwrite
// the variables within this function.
(function() {
  const name = "Mark";
  const age = 26;

  console.log(name); // Mark
})();

console.log(name); // KEVIN

What’s currying?

Currying lets you transform a function with more than one argument into a function with just one argument. Here's a "simple" example:

function notCurried (a, b) {
  return a + b
}

function curried (a) {
  return function (b) {
    return a + b
  }
}

notCurried(2, 2)   // 4
curried(2)(2)      // 4
var x = curried(2)
x(2)               // 4

While the above example seems a little obscure, we see currying in certain places in JavaScript, like Redux's connect function:

export default connect(mapStateToProps)(exampleApp)

What does single-threaded mean, and why is it important?

Single-threaded means that JavaScript only processes a single piece of code at a time. Therefore, in order to make an application seem fast to the user, you want to make sure your code isn't blocking. For instance, if you need to call another server, you want to perform that call asynchronously or your code will hang for an indeterminate amount of time.

What is the event loop in JavaScript?

The event loop helps JavaScript perform async operations. When you need to wait for async code to complete, it gets pushed to the event loop (which handles it and hands you the result when it's done).

What are the differences between var, let, and const in JavaScript?

If possible, opt for let and const for the reasons discussed below.

var

A variable declared with var...

  • is scoped to its containing function or the global scope
  • can be redeclared like the example below
var example = 1
var example = 2
console.log(example) // 2
// Contrast this with "let" below
var a = "foo"
if ("some truthy condition") {
  var a = "bar"
}
console.log(a) // "bar"

let

A variable declared with let....

  • is scoped to its containing block ({})
  • cannot be redeclared within its scope (this is why they're a safer choice than var)
let example = 1
let example = 2 // causes error
example = 2     // but this is fine
// Contrast this with "var" above
let a = "foo"
if ("some truthy condition") {
  let a = "bar"
}
console.log(a) // "foo"

const

A variable declared with const...

  • is scoped to its block ({}) or the global scope
  • maintains a constant value and cannot be updated
const example = 1
const example = 2 // causes error
example = 2       // also causes error

It's smart to prefer let and const over var, as they are block scoped, so you're less likely to run into issues. const is a smart choice for any variables that should not be updated. In practice, I find that const is ideal 90% of the time.

What is short-circuit evaluation?

Short-circuit evaluation is one of my favorite features of JavaScript. It's an evaluation mechanism that uses the || (OR) operator. It will try, from left to right, to find a truthy value. If it can find one, it considers the statement true; if not, it considers it false.

const result = false || true
console.log(result) // true
const result = true || false
console.log(result) // true

What is "blocking" in JavaScript?

A blocking event is an event that stops the rest of your code from running. JavaScript only handles one piece of code at a time, so if a blocking event is taking a long time to process, none of the other code can start until it's done. This can make your application feel slow and janky. It's much better to make long operations asynchronous (by taking advantage of JavaScript's event loop). This helps your application run more smoothly.

How does Node.js differ from client-side JavaScript?

Node runs JavaScript on a server. It's the V8 engine bundled with some other libraries so it can perform things like file I/O and networking. Client-side JavaScript, on the other hand, runs in a browser to handle user interactions, requests to the server, etc.

What is the “use strict” declaration and why is it important?

You can write "use strict"; at the top of a .js file or the beginning of a function to indicate that the JavaScript should be executed in strict mode. Strict mode helps you write cleaner/more maintainable/less buggy code in a lot of different ways – for instance, it prevents you from using undeclared variables. It also future proofs your code by disallowing you to use future features as names (for instance, let was one of the disallowed words).

What is object destructuring?

Destructuring is shorthand for pulling values from an object. For instance, if you have an object...

const character = {
  name: "Link",
  profession: "Hero",
  hometown: "Kokiri Forest"
}

...and you want to only pull a few values from that object, you can use destructuring. In other words, this...

const { name, profession, hometown } = character

...is the shorthand for this:

const name = character.name
const profession = character.profession
const hometown = character.hometown

This is especially useful in React. For instance, you can destructure props so you don't need to prepend it to everything in your components:

// A component with destructured props
const Example = ({ text, color }) => (
  // ...
)

// A component without destructured props
const Example = props => {
  const text = props.text
  const color = props.color
  return (
    // ...
  )
}

And for another example, let's destructure the state in the render function:

class Example extends Component {
  state = {
    x: 0,
    y: 0
  }

  render () {
    const { x, y } = this.state
    return (
      // ...
    )
  }
}

What is an interpolated expression (template string, template literal)?

Template strings are a feature added by ES6. They're a way to write strings without needing to use the verbose syntax from earlier versions of JavaScript. You can make one by surrounding a quote in back ticks, and you can also use ${} to embed a variable directly into a string. For instance:

const name = "Mark"
const message = `Hello ${name}, I hope you're having a good day!`

You can even do something fancier like:

const message = `You are ${ currentUser ? 'logged in' : 'not logged in' }.`

Web security

Explain XSS. How can you prevent it?

XSS, "cross site scripting", is where a third-party injects JavaScript to run on a website that they don't own. One of the biggest XSS attacks I can think of is the Samy worm that spread to 1M+ MySpace users.

All modern browsers prevent CORS (cross origin resource sharing) unless explicitly told not to. But it's also smart to think of any user-entered data as potentially malicious. Make sure that data is:

  • escaped — secured prior to rendering for the end user
  • sanitized — strip all tags, make sure the input is UTF-8 encoded, etc.
  • validated — the type of value you're expecting – for instance, a database shouldn't save <script>...</script> for a date field

Explain what CSRF is. How do you prevent it?

CSRF stands for "cross site request forgery". Let's say you're logged into an application that's vulnerable to a CSRF attack. An attacker could get you to click on a link – for instance, in an email:

"Hey there [YourName], Not sure if you're interested, but we're throwing a gaming night for JS developers on Friday. Free pizza and beer! Wanted to give you an invite. Here's the list of games."

The link could lead to their attack server where they can perform a POST/DELETE/etc. request to your server and perform a malicious action (removing records, posting spam, deleting your whole account, etc).

CSRF attacks are subtle/easy to perform because the attacker doesn't need to compromise your browser. They just need to make you visit a website, which is basically just social engineering. (And if you think you won't fall for it, you'd be surprised — what if the attacker gains access to your friend's email? Or your coworker's? Or someone in your family?)

If we use cookies to store user sessions on our app, we're vulnerable. To defend against CSRF attacks, you can create a timed security token for each user. You can add this token as a hidden field on your forms and check against it on post requests. You'd want to make sure that your application only allows requests where the security token matches the one it has on record. Frameworks like Ruby on Rails handle this automatically.

You can also mitigate the risk with SameSite cookies, but they're not yet supported on every browser.

Where should you perform form validation?

To ensure the integrity of your data, perform validation on the server. Never trust the client. This is because the client (the user and their browser) has complete control over the code that runs on their machine. They can edit their version of your page – the HTML, CSS, and JavaScript – to send you data they shouldn't have been able to enter. The server, on the other hand, is controlled by you.

To provide a better user experience, you can also add validation to the front end. Validate data on the client not to ensure its integrity, but just to help users catch errors before they submit the form (maybe they forgot to enter their email).

Other

What is an API?

API stands for "application program interface". It lets you access (or control) a program that someone else wrote with your own application. So I could make a writing application that, for instance, lets people post to their WordPress blog with the WordPress REST API, or push something to GitHub with the GitHub API, or even charge someone's card with the Stripe API.

What is REST?

REST is an approach to interacting with data. It stands for "REpresentational State Transfer". It makes the assumption that in most applications, you just want to Create, Read, Update, or Delete records (CRUD operations).

What is a memory leak, and how could you cause one?

Your application uses memory when it runs. When your application no longer needs this memory, it should free it up. However, with memory leaks, the application holds onto memory that it no longer needs.

JavaScript is a garbage-collected language which means that it tries to help us manage memory. However, memory leaks can still pop up through timers (like setInterval), global variables, etc.

You can use the "Memory" tab on Chrome DevTools to identify memory leaks. And if you're developing in something like React, you'll also see warnings that can help you avoid leaks (like unmounting a component with a setInterval without clearing the interval beforehand).

What are the benefits/tradeoffs of using WebP images?

WebP is an image format from Google. It's 25% to 35% smaller than PNGs and JPGs. It's not yet supported on every browser (about 73% of users can use it), but it's great for image-heavy websites.

That being said, some users don't like that they can't download and view WebPs like other images (you have to download and install your own codec to be able to view them). So, while they can provide cost savings/bandwidth savings/faster speeds, we also need to take users into consideration when evaluating a switch.

Digital Ocean has a great guide on using WebPs here.

What's the benefit of unit testing/automated testing?

There are a number of benefits to unit testing/automated testing:

  1. Unit tests help us refactor code more efficiently. If we weren't running any tests on a large project, we might edit a feature and unknowingly affect other parts of the application.
  2. Unit testing can help detect us detect bugs and improve the overall quality of our code.
  3. As a nice side effect, tests also provide documentation for developers who are new to the project. By looking at tests, we can determine what a particular piece of code is supposed to do.

What are the benefits of test-driven development (TDD)?

With test-driven development, we write unit tests before we start writing actual code. This forces us to define exactly what results we expect from a particular piece of code. This approach forces you to determine what "success" looks like before you actually start the work – like a spec sheet.

Another benefit of test-driven development is that you're requirement-focused instead of code-focused. For instance, some developers who write tests after code is already written may have a tendency to custom-fit the tests to the working code instead of the project requirements. This doesn't happen with everyone, but it's good to prevent the chance of it occurring altogether.

Conclusion

If you have any comments, please send me an email at m@mtm.dev.

If you're worried about landing that next job, just remember that no interviewer expects you to be perfect. Just try to find the parts of this guide that seem fun to you, and branch out from there. I believe you can do this!