7 Concepts you should know in modern Javascript before learning React
At Doctolib, and many other places in the tech industry, we use React intensively. We are also happy to welcome enthusiastic people new to the stack. But jumping straight into React can be daunting if you only know that javascript is dynamically typed, or think that javascript is like any other OO programming language such as Java, C# or C++ (which it isn’t really). As React is just plain javascript at its core, having a good grasp in modern (ES6) Javascript will give you a solid foundation for the former. So let’s dive into some of the building blocks of the language.
Data type system
There are nine different types in Javascript.
- Primitives:
undefined
,Boolean
,Number
,String
,BigInt
,Symbol
, andnull
¹ - Structural types:
Object
andFunction
The type of a variable can be checked by the operator typeof
:
typeof(undefined) // undefined
typeof(1) // number
typeof("hello") // string
typeof(BigInt(1)) // bigInt
typeof(Symbol("ok")) // symbol
typeof(null) // object ???
typeof({}) // object
typeof([]) // object
typeof(() => {}) // function
Objects are the bread and butter of Javascript. Master them! An object is a dictionary that points a property to its value. Properties must be either String, Numbers or Symbol. Values can be of any type. Note that Arrays are actually objects in Javascript.
let person = { firstName: 'Jon', lastName: 'Snow' }
person.firstName // Jon
person['firstName'] // Jon
typeof(person) // object
Object instantiation and classes
Have you ever instantiated (or created) an object with the new
keyword in a Programming Language? Well, you can do it in Javascript too. And there are two built-in ways of doing it: Function and Class.
Function:
function Person(firstName, lastName) {
this.firstName
this.lastName
this.greet = () => {
console.log(`Hello I'm ${firstName} ${lastName}`)
}
}
let person = new Person('Jon', 'Snow')
Class:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
greet() {
console.log(`Hello I'm ${this.firstName} ${this.lastName}`)
}
}
let person = new Person('Jon', 'Snow')
I won’t enter into details but there are some differences between the two, but they essentially serve the same purpose (i.e the instantiation of an object), the class version being more object oriented like, and newer.
Prototypes
Inheritance allows an object to use the properties of another one. In Javascript, this is achieved using prototyping. If you are familiar with this concept from Java or C++, your best option is to forget everything as the inheritance system is substantially different. Every object has a hidden property called __proto__
, this property can point to another object, the former inheriting the properties of the latter. Updating the "mother" object has a direct impact on the "child" object, since this is just a reference. We also can form a chain of inheritance using multiple objects pointing one to the other.
let user = { login: 'jsnow'}
let admin = { isAdmin: true }
let root = { isRoot: true }admin.__proto__ = user
root.__proto__ = adminroot.login // jsnow
root.isAdmin // true
root.isRoot // true
user.login = 'jdoe'
root.login // jdoe
But this is rather an uncommon technique to use inheritance (although it’s a great way to understand it). Practically know that they are different ways of doing it and it mainly involves functions.
Scope and closure
The concept of scope and closure are related concepts, and actually simpler than you think. A scope holds the variables that can be used in that scope. Commonly we usually talk about local and global scope. A closure is just the ability to access the outer scope of an (inner) function. So you are using closure every day when using a global variable inside a function. More generally each inner function has access to the variables of its parent function, and the ones of the parent function of the parent function, all the way up to the global scope. Each function’s own scope is said to be linked to a lexical environment which is a mapping between a variable’s name and its value.
let b = 2 // global variablefunction foo() {
let a = 1 // local variable to foo
console.log(c)
}let c = 3 // other global variable
foo() // prints 3 since foo has access to the global scope
console.log(a) // throws a ReferenceError since a is local to foo
Note that, despite appearances, variable c
is not actually used before it is fully declared and assigned. This also means that the inner scope can access the outer scope variable if it is ready in memory.
Modules
A module is just a .js
file. No big deal? Not quite so. A module is loaded using export and import keywords. A module works in strict mode, which among other things, make your code throw an error when it wouldn’t without it (i.e assigning to an undeclared variable throws an error in strict mode). Each module has its own top-level scope and can’t be used outside unless exported explicitly. A module code is executed only once, with the first import. If multiple modules import variables of a module, the code is only executed once for the first import. Then the resulting variables are shared with the other modules.
In helper.js
// in helper.jsconsole.log('I will be executed only once but imported twice')
console.log('in helper')
export const foo = (arg) => { /** ... **/}
In Patient.js
// in Patient.js import { book } from './Appointment'
import { foo } from './helper'console.log('in Patient')
In Appointment.js
// in Appointment.js import { foo } from './helper'export const book = () { /** ... **/ }
console.log('in Appointment')
One million dollars question: what is the order of the console logs if you start by importing Patient?
I will be executed only once but imported twice
in helper
in Appointment
in Patient
Concurrency model and event loop
Javascript runs in a single thread. Whether you’re writing an event handler to a button click, a callback to an external service, or a periodic UI refresh using setInterval
, all these different pieces of code you’ve written cannot run concurrently. You can still get in trouble as the order on which your different callbacks will run is not guaranteed. When an asynchronous function like an async fetch is called, it starts an action executed by the browser API in a separate thread (don’t get confused here, the underlying async action is technically not your code but part of that built-in browser API). Upon completion of the asynchronous call, the onSuccess function (supposing you’ve coded one) is put into a work queue waiting to be processed. The event loop is then responsible for putting the next function to be executed in the call stack.
Memory management
The javascript engine automatically destroys unused variables from memory: this process is called garbage collection. Implementation details between engines may vary, but this will give you the bigger picture. The algorithm basically works in two phases: “mark and sweep”. At a given point in time, references that are considered reachable are marked as roots. This typically consists of all global variables. Once it is done, for each root variable, the algorithm traverses a dependency tree starting from that root and marks every reference it discovers. All references which are not yet marked are considered not reachable and destroyed from memory.
Conclusion
Congratulations on reading so far! Writing Javascript may flow naturally yet some of its corners are tricky. After reading this (and maybe this), you will be much more equipped and prepared for learning client side Javascript frameworks such as React. While frameworks may come and go, the foundations of their underlying languages will remain the same. So I encourage you to strengthen them in parallel as it helps you truly understand what’s going on under the hood.
[1] is a primitive but typeof(null)
weirdly returns object
!!!.
If you want more technical news, follow our journey through our docto-tech-life newsletter.
And if you want to join us in scaling a high traffic website and transforming the healthcare system, we are hiring talented developers to grow our tech and product team in France and Germany, feel free to have a look at the open positions.
Special thanks to Alexandre Ignjatovic, Emmanuel Gautier and Quentin Menoret for reading the draft and Lana Petersen for the cover picture.