Categories
JavaScript Best Practices

JavaScript Best Practices: More Things to Avoid

ike any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are lots of tricky parts to JavaScript, so there are many things to avoid.

We can follow some best practices to make our JavaScript code easy to read.

In this article, we look at more constructs that we should avoid, including the misuse of the for...in loop, the == operator, the arguments object, and this.


Misusing the For…in Loop

The for...in loop shouldn’t be used to iterate through arrays and array-like objects. For example, if we have:

const arr = ['a', 'b', 'c', 'd', 'e'];
for (let i in arr) {
  console.log(i)
}

We get back:

0
1
2
3
4

However, this is a misuse of the for...in loop since it’s supposed to iterate through the enumerable properties of an object and its prototype rather than looping through the indexes of an array.

The order of the enumeration isn’t guaranteed, so we might not loop through the array in order, which is bad.

The for...in loop also loops through inherited properties that are enumerable, which is another problem.

For example, if we have:

Then we create an object with a prototype object with the foo property. The for...in loop will loop through both the enumerable properties of obj’s prototype and obj.

To avoid looping through the properties of its prototype, we can use Object.keys instead:

for (let prop of Object.keys(obj)) {
  console.log(prop);
}

We can loop through the key-value pair of obj without anything from its prototype, we can use Object.entries:

for (let entry of Object.entries(obj)) {
  console.log(entry);
}

For looping through arrays and array-like objects, we should use the for...of loop instead:

const arr = ['a', 'b', 'c', 'd', 'e'];
for (let a of arr) {
  console.log(a)
}

Then we get the entries of the array.


Messing With this

The this object is always a problem in JavaScript. This is because it changes depending on the scope.

Fortunately, in ES6, we have arrow functions that don’t change the value of this if we reference it inside. This also means that we can’t change the value of this with call, bind, or apply.

For example, the following code will log the window object:

The value of this doesn’t change even if we try to change it with bind.

Also, ES6 has the class syntax for creating constructor functions so it’s clear that this should be inside the class as long as we use class methods and arrow functions.

Class methods will have the class as the value of this and arrow functions inside the class will have the same value for this.

Photo by Casey Allen on Unsplash


The == Operator

The == operator does automatic type conversion before comparing the operands for equality. This creates issues with different types of data being considered the same.

For example, null == undefined returns true, which we probably don’t want.

null == undefined returns true because they’re both falsy.

To avoid these kinds of issues, we should use the === operator instead, which would avoid comparisons of these kinds of operands returning true. This is because === checks the type of each operand in addition to the operand’s contents.


The arguments Object

With the introduction of the rest operator, we can finally avoid the use of the arguments object for getting the arguments that were passed into a function.

The arguments object shouldn’t be used for a few reasons. One is that it’s an array-like object, which means properties like length are in it and it can be looped through with a for loop, but array methods like map and forEach aren’t included in it.

Also, we can access its entries with its index, which is deceiving since it’s not actually an array.

Using arguments also prevents any code optimization by browser engines, which means performance is slower. Also, the arguments object isn’t available in arrow functions.

Therefore, to access extra arguments that are beyond the parameters listed, we should use the rest operator instead.

For example, instead of writing:

We can write:

const add = (...args) => args.reduce((a, b) => a + b, 0);
console.log(add(1, 2, 3, 4, 5));

Notice that it’s much shorter to use the rest operator, which is the ...args part of the function signature. args is already an array so we have all the array methods available for us to use.


Conclusion

In modern JavaScript, we can abandon a lot of older constructs to make our code easier to read and maintain.

We can use the rest operator instead of the arguments object. This is because the rest operator gives us an array of arguments instead of the array-like arguments object.

Also, we should avoid the == operator for equality comparison since it does automatic type conversion before comparing which we might not want.

We should also avoid messing with this by using arrow functions and class syntax for constructor functions. These two constructs make the value of this a lot clearer. We only use class methods for classes and arrow functions for functions that aren’t a method of a class.

Finally, the for...in loop shouldn’t be used for looping through arrays and array-like objects since the order of enumeration isn’t guaranteed to be in order, leading to unexpected results.

Categories
JavaScript Best Practices

JavaScript Best Practices — Using Libraries and Shortcuts That are Clear

Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are a lot of tricky parts to JavaScript, so there are things we should avoid that reduce the quality of our code. By following best practices, we can create elegant and manageable code that’s easy for anyone to work with.

In this article, we look at using libraries, using shortcuts that make sense, short ways to build a string from an array, and being aware of the difference between development and live code.

Use Libraries When They Exist

If somebody already made a library to do something that we want to do, we should use the library instead of writing everything from scratch again.

Reinventing the wheel is a time-consuming process, then testing all the cases takes even longer.

This means that we should use libraries to speed up software development and not reinvent the wheel.

Nowadays, client-side apps are more complex than ever, with users expecting slick user experiences in the apps we write. This means writing everything from scratch is pretty much impossible.

This is why frameworks like Angular, React and Vue are becoming more and more popular. There’s no going back to the bad old days where everything is written in plain JavaScript.

Front end development isn’t just adding a few things on top of our back end apps anymore.

However, when we’re learning JavaScript, we should still learn by writing plain JavaScript code first to learn the principles that make these libraries and framework work.

Nowadays, client-side apps are complex enough that it’s hard to avoid using lots of libraries to build something functional.

Difference Between Development Code and Live Code

The codebase that we read and write shouldn’t be mistaken for the code that’s run.

Most browsers optimize the code if we follow the best practices that are listed all over the online community.

Browsers convert the code we write to machine code on run-time to make it run faster.

Also, lots of client-side apps now have a build process associated with it to optimize the code by minifying it and combining redundant code into one place, etc.

This means that we should build code for readability and maintainability rather than trying to make hard to read code that we think will make it run faster.

Browsers no longer run code line by line from script files anymore.

Use Shorthand When They Make Sense

Some of the older shorthands in JavaScript are confusing. For example, if we run the following code:

if(false)  
   var x = false  
   let y = 0  
console.log(x, y)

We get undefined and 0 logged. undefined makes sense, but why is y 0? This is because the declaration of y actually doesn’t belong to the if block.

As we can see, the if statement without curly braces is confusing. Only the first line after the if statement is considered to be inside the if block.

Therefore, we should always wrap our if blocks with curly braces.

Another problem with the code snippet above is that we don’t have semicolons. This is another problem because when don’t know where the line ends. It’s better to just make it clear by putting in the semicolon at the end.

However, since ES6, we have lots of shorthands that are very useful and aren’t confusing.

For example, the spread and rest operators are very useful for copying or combining objects and getting arguments in an array respectively.

The spread operator can be used as follows:

const obj1 = { foo: 1, bar: 2 }, obj2 = { baz: 3 };  
const obj3 = { ...obj1, ...obj2 };

Then we get obj3 having the value:

{ foo: 1, bar: 2, baz: 3 }

The rest operator uses the same 3 dots, but works on functions. For example, we can write a function with the rest operator as follows:

const add = (a, b, ...rest) => {  
  return a + b + rest.reduce((a, b) => a + b, 0);  
}

Where ...rest is has an array with the arguments other than the first 2.

Then we can call it as follows:

console.log(add(1, 2));  
console.log(add(1, 2, 3));  
console.log(add(1, 2, 3, 4, 5));

And we get 3, 6, and 15 respectively.

Another handy and clear shortcut is the destructuring assignment syntax. It works on both objects and arrays. For example, we can destructure an array into variables as follows:

const [a, b] = [1, 2];  
console.log(a, b);

Similarly, with objects, we can write:

const {  
  foo,  
  bar  
} = {  
  foo: 1,  
  bar: 2  
};  
console.log(foo, bar);

We get 1 and 2 for a and b respectively in both examples.

Shortest Way to Building a String

There’re multiple ways to build a string from an array of strings in JavaScript. One way is to use a loop as follows:

const arr = ['foo', 'bar', 'baz'];  
let str = '';  
for (let i = 0; i < arr.length; i++) {  
  str += arr[i];  
  if (i !== arr.length - 1) {  
    str += ',';  
  }  
}

The other way is to use the join method built into array objects:

const arr = ['foo', 'bar', 'baz'];  
let str = arr.join(',');

As we can see, the loop is much more complex and are much more prone to errors because of it. We’ve to loop through the array, concatenate each entry, and then add a comma after it if it’s not the last entry.

On the other hand, the join method just joins the array entries with a comma with one line.

Today’s JavaScript apps are more complex than ever. This is why we should use libraries and frameworks to build apps to make our lives easier.

This also means that we should be writing code that makes development easy rather than thinking about how to accommodate browsers. However, we should still think about performance when writing the code.

Also, we should also use shortcuts that makes development and reading the code easy. Most shortcuts introduced with ES6 or later have both of these characteristics. Older shortcuts like writing if statements without curly braces are not.

Finally, we should use existing methods to simplify code. For example, when making a comma-separated string from an array of strings, we can use the join method instead of writing a loop to do it.

Categories
JavaScript Best Practices

JavaScript Best Practices — Browser Differences, Performance and More

Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are a lot of tricky parts to JavaScript, so there are things we should avoid that reduce the quality of our code. By following best practices, we can create elegant and manageable code that’s easy for anyone to work with.

In this article, we’ll look at how to gracefully support old browsers, avoiding heavy nesting, various optimizations, and checking the data in our programs.

Progressive Enhancement

Progress enhancement means that we should degrade our app gracefully when some technology isn’t available. This means that we should check if some technology is supported in all the browsers that we want to support and keep our app working in some way if it doesn’t.

We can also add polyfills to add support for new technologies that older browsers that we support don’t have.

For example, if we want to use new array methods that aren’t available in Internet Explorer 11 but our app still supports the browser, then we have to add a polyfill for it or check for its existence and do something different instead of crashing.

Avoid Heavy Nesting

Having lots of nesting in code makes them very confusing to read. This is because it’s very hard to follow the logic of the code.

Nesting conditional statements and loops should be kept to a minimum.

For example, instead of writing:

const items = {
  foo: [1, 2, 3],
  bar: [1, 2, 3],
  baz: [1, 2, 3]
};

const parentUl = document.createElement('ul');
for (const item of Object.keys(items)) {
  const parentLi = document.createElement('li');
  const childUl = document.createElement('ul');
  for (const num of items[item]){
    const childLi = document.createElement('li');
    childLi.textContent = num;
    childUl.appendChild(childLi);
  }
  parentLi.textContent = item;
  parentLi.appendChild(childUl);
  parentUl.appendChild(parentLi);
}
document.body.appendChild(parentUl);

This creates a nested list, which is confusing to read and write. We should reduce nesting by separating the list creating into a function and call the function instead:

const items = {
  foo: [1, 2, 3],
  bar: [1, 2, 3],
  baz: [1, 2, 3]
};

const createUl = (items) => {
  const ul = document.createElement('ul');
  for (const item of items) {
    const li = document.createElement('li');
    li.textContent = item;
    ul.appendChild(li);
  }
  return ul;
}

const parentUl = createUl(Object.keys(items));
const parentLis = parentUl.querySelectorAll('li');
for (const parentLi of parentLis) {
  const childUl = createUl(items[parentLi.textContent]);
  parentLi.appendChild(childUl);
}
document.body.appendChild(parentUl);

As we can see, the code above doesn’t have any nested loops, which makes the code easier to read. Also, we have a createUl function to create the ul element with entries inside and returns the ui element object.

This means that we can attach it however we like to the document or an HTML element afterward.

Optimize Loops

We should cache values that are used in every literation in a single variable.

This is because every time we do this, the CPU has to access the item in memory again and again to compute its results.

Therefore, we should do this as little as possible.

For example, if we have a loop, we shouldn’t write the following:

for (let i = 0; i < arr.length; i++) {

}

Instead, we should write:

let length = arr.length;
for (let i = 0; i < length; i++) {

}

This way, arr.length is only referenced once in our loop instead of accessing it in every iteration.

Photo by Adrien CÉSARD on Unsplash

Keeping DOM Access to a Minimum

DOM manipulating is a CPU and memory-intensive operation. Therefore, we should strive to keep it to a minimum.

This means we have to keep pages as simple as possible and only do DOM manipulation when it’s necessary. Any static styles should be in CSS and not added on the fly with JavaScript.

Also, we should keep any static elements in HTML and not create them by manipulating the DOM.

Also, we should make functions that create elements and call them when we need to rather than continuously doing DOM manipulating operations on the top-level of the code.

Write Code for All Browsers

All browsers should get the same treatment by our code. We shouldn’t write hacks to accommodate various browsers because these hacks will be broken quickly when the browser changes versions.

We should stick to code that’s are accepted as standards or use libraries like Modernizr to deal with issues with different browsers.

Also, we can add polyfills to add any functionality that is missing in various browsers, so we can keep our app running on different browsers even though there’s they might not support some functionality out of the box.

Don’t Trust Any Data

We should check for any data that’s inputted by the user. HTML5 has lots of form validation functionality to check for valid inputs. We can do it with HTML5 and plain JavaScript.

Once we check for the inputted data, we also need to check for data in variables and returned from functions. Since JavaScript is a dynamically typed language, we’ve to check for these things.

For primitive values, we can use the typeof operator to check the data type of data. For example, if we have:

let x = 1;

Then typeof x will return 'number' . Other primitive data types like boolean, strings, undefined, etc. are the same.

The only exception is null , which has the type object.

We should always check for values like null or undefined since they might crash our program. We can do that by writing x === null and typeof x === 'undefined' respectively.

Also, we should be careful of type coercions done by JavaScript, like in conditional statements and function calls. For example, the Math.min method converts its arguments to numbers when it’s called. The == operator converts all the operands to the same type before returning the result.

For objects, we can check for their type by using the instanceof operator to see which constructor they’re created from. For example, if we have an array:

let arr = [];

Then [] instanceof Array will be true . Arrays also have a static isArray method to check for the data type.

In the JavaScript code, we write, we should be aware of the differences in different browsers that our app supports. This means checking if the methods that we want to use exist and adding polyfills to add missing functionality for older browsers.

We should cache variables and properties that are accessed repeatedly in loops so they don’t have to be accessed each time the loop runs.

Deep nesting should also be avoided to keep code clear and easy to follow.

Also, since DOM manipulation is an expensive operation, it should be kept to a minimum. Static styles and elements should be in CSS and HTML respectively.

Finally, we shouldn’t trust the data in our apps. Inputs have to be checked for format and validity, and data in our variables and values should be checked for type, including null or undefined .

Categories
JavaScript Best Practices

JavaScript Best Practices — Performance

Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are a lot of tricky parts to JavaScript, so there are things we should avoid that reduce the quality of our code. By following best practices, we can create elegant and manageable code that’s easy for anyone to work with.

In this article, we’ll look at ways to improve the performance of our apps. Actions include caching data in variables, using the fastest way to loop through variables, reducing DOM access and elements on the page, and deferring script loading.

Reduce Access to Variables and Properties

We should reduce the times that we access variables and object properties in our apps.

This is because every time we do this, the CPU has to access the item in memory again and again to compute its results.

Therefore, we should do this as little as possible.

For example, if we have a loop, we shouldn’t write the following:

for (let i = 0; i < arr.length; i++) {

}

Instead, we should write:

let length = arr.length;
for (let i = 0; i < length; i++) {

}

This way, arr.length is only referenced once in our loop instead of accessing it in every iteration.

Fastest Way to Loop Through Variables

In JavaScript, there’re multiple ways to iterate through iterable objects. One is the good old for loop. Other ways include the for...of loop, the forEach method for arrays. map and filter also loop through the array as the map and filter operations are being done. There’s also the while loop.

Of all the ways to run loops, the for loop is the fastest way, with or without caching the length like we did above. Caching the length sometimes makes the loop performs better, however.

Some browser engines have optimized the for loop without caching the length property.

The while loop with decrementing index is approximately 1.5 times slower than the for loop

Using the forEach loop is 10 times slower than the for loop, so it’s probably better to avoid it, especially for large arrays.

We can see the results here.

Reduce DOM Access

Accessing the DOM is an expensive operation since the browser has to grab the element from the web page and then create an object from it and return it.

To reduce DOM access, we should set the DOM Node objects to a variable if we need to manipulate it more than once.

For example, if we have the following HTML and we want to set some text to it after a few seconds:

<p id='foo'>

</p>

We can write the following code to do so:

const setText = (element, textContent) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      element.textContent = textContent;
      resolve();
    }, 3000)
  })
}

(async () => {
  const foo = document.querySelector('#foo');
  await setText(foo, 'foo');
  await setText(foo, 'bar');
  await setText(foo, 'baz');
})();

In the code above, we have one function which gets the HTML element that we want to manipulate, and the text content that we want to set.

The setText function returns a promise to set the text to a given element after 3 seconds.

Then we have an async function to set the text 3 times. The important part is that we pass in the reference to the element in each call. This way we don’t have to get the element from the web page 3 times, which is an expensive operation.

Photo by Sawyer Bengtson on Unsplash

Reduce DOM Size

Rendering the DOM tree is slow. Therefore, we have to reduce the size of the tree.

There’s no way around it but to make our web pages as simple as possible.

Having a smaller DOM make searching for elements with methods like querySelector, getElementById, or getElementsByTagName faster since there’s less to look for.

Also, page rendering performance will improve since there’s less to load. This is especially true for slower devices like phones and tablets.

Don’t Declare Unnecessary Variables

Every time we declare variables, the browser has to reserve memory space for the variables. Therefore, to reduce memory usage, we should declare too many variables.

For example, if we have the following HTML:

<div id='foo'>
  <p>

  </p>
</div>

And we want to set the text content of the p element, we shouldn’t write something like:

const foo = document.querySelector('#foo');
const p = foo.querySelector('p');
p.textContent = 'foo';

since we have 2 variables. This means our computer has to store the values for 2 more JavaScript variables.

Instead, we can reduce to no variable declarations by writing:

document.querySelector('#foo p').textContent = 'foo';

As we can see, we can use the querySelector method to select anything with CSS selectors. This means that we should use this method and the related querySelectorAll method to select elements since they can both use CSS selectors to select any HTML element node.

Defer the Loading of Scripts

Loading JavaScript files is an expensive operation. The browser has to download the file, parse the content, and then convert it to machine code and run it.

The browser will download one file at a timeline by line, so it’ll hold up any other operation from happening.

Therefore, we should delay it as much as we can. We can do this by putting the script tag to the end. Also, we can use the defer attribute on the script tag to do this.

Also, we can run scripts after the page is loaded by create script elements on the fly and appending it as follows:

window.onload = () => {
  const element = document.createElement("script");
  element.src = "https://code.jquery.com/jquery-1.12.4.min.js";
  document.body.appendChild(element);
};

Anything that can be loaded after the page loads can use this method of script loading.

We can speed up our pages by doing a few things. First, we can cache data in variables so we don’t have to access them repeatedly. Then we can loop through items faster with the for loop.

Also, we can reduce the DOM size to reduce the items that need to be loaded. We can also cache DOM objects in our code by assigning them to variables.

We also should not declare unnecessary variables, and we should defer the loading of scripts as much as possible so it won’t hold up our browser.

Categories
JavaScript Best Practices

JavaScript Best Practices — DOM Manipulation and Functions

Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are a lot of tricky parts to JavaScript, so there are things we should avoid that reduce the quality of our code. By following best practices, we can create elegant and manageable code that’s easy for anyone to work with.

In this article, we’ll look at ways to reduce DOM manipulation, use shortcut notation, and having functions perform a single task.

Avoid DOM Manipulation

In our JavaScript, we should avoid manipulating the DOM if we want to display something dynamically. We can easily put a lot of styling code in our CSS instead of manipulating it directly with JavaScript.

For example, if we want to make an input display a red border when the input is invalid on submit, we can write the following HTML code:

<form>
  <input type='text' required>
  <button type='submit'>
    Submit
  </button>
</form>

Along with the following JavaScript code:

const input = document.querySelector('input');
const form = document.querySelector('form');

form.onsubmit = (e) => {
  e.preventDefault();
}

input.oninvalid = () => {
  input.style.borderColor = 'red';
  input.style.borderStyle = 'solid';
  input.style.borderWidth = '1px';
}

As we can see, in the event handler we assigned to the oninvalid property of the input, we set the border with JavaScript.

However, we can instead do it with CSS by creating a class and setting the class name in JavaScript instead.

We keep the same HTML code, but add the following CSS code:

.invalid {
  border: 1px solid red;
}

Then in our JavaScript code, we replace what we had with:

const input = document.querySelector('input');
const form = document.querySelector('form');

form.onsubmit = (e) => {
  e.preventDefault();
}

input.oninvalid = () => {
  input.className = 'invalid';
}

As we can see, it does the same thing in a much shorter way. It requires less processing power since it isn’t setting the styles dynamically.

Also, now we have a class and style that we can reuse in other HTML elements so that we don’t have to repeat code.

Using Shortcuts

In JavaScript, there are lots of shortcuts that are still clear for developers to read.

For example, when we create an object, we can either use the Object constructor or the object literal notation.

With the Object constructor, we can create an object as follows:

let obj = new Object();
obj.foo = '1';
obj.bar = '2';
obj.baz = function() {
  console.log('baz');
}

Alternatively, we can write the same thing in the object literal notation:

let obj = {
  foo: '1',
  bar: '2',
  baz() {
    console.log('baz');
  }
}

We have the same number of lines, but we have to repeat the object name in every line in the first example. Whereas we don’t have to do that with the object literal.

Object literals also improve clarity, so we can use that instead of the Object constructor to create an object. They both do the same thing, but we don’t have to repeat code as we did by creating an object with the Object constructor.

Likewise, instead of defining an array as follows:

let arr = new Array();
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;

We can define it in a much shorter way by writing:

let arr = [1, 2, 3, 4, 5];

As we can see, it’s way shorter to define it with the array literal than the Array constructor. Also the Array constructor is confusing since it has 2 signatures. If we pass in one argument, then we get the array with the length set by the argument. Otherwise, we get an array with the arguments we passed in as the content.

Another handy shortcut is the ternary operator, where we can assign things to a variable conditionally.

For example, instead of writing:

const x = 100;
let foo;
if (x === 100) {
  foo = 'bar';
} else {
  foo = 'baz';
}

We can write:

const x = 100;
let foo = (x === 100) ? 'bar' : 'baz';

In the code above:

let foo;
if (x === 100) {
  foo = 'bar';
} else {
  foo = 'baz';
}

is the same as:

let foo = (x === 100) ? 'bar' : 'baz';

They both check if x is 100 then, then assign 'bar' to foo if it is, and assign 'baz' otherwise.

The code is much shorter with the ternary operator and we get the same result. It also doesn’t make the code harder to read.

Photo by Austin Distel on Unsplash

One Function Per Task

Generally, a function should only do one thing. This makes each function short and easy to read.

It also makes reusing it easier since there’s less chance of conflicting functionality in each function.

Also, it’s a good idea to create helper functions for common tasks.

For example, if we want to create some elements on the fly on a page. Either we can write:

const createPage = () => {
  const items = ['foo', 'bar', 'baz'];
  const ul = document.createElement('ul');
  let li;
  for (const item of items) {
    li = document.createElement('li');
    li.textContent = item;
    ul.appendChild(li);
  }
  document.body.appendChild(ul);

  const googleLink = document.createElement('a');
  googleLink.href = '[http://www.googole.com'](http://www.googole.com%27);
  googleLink.textContent = 'Google';
  document.body.appendChild(googleLink);

  const yahooLink = document.createElement('a');
  yahooLink.href = '[http://www.yahoo.com'](http://www.yahoo.com%27);
  yahooLink.textContent = 'Yahoo';
  document.body.appendChild(yahooLink);
}

createPage();

Or we can write:

const createLink = (textContent, href) => {
  const link = document.createElement('a');
  link.href = href;
  link.textContent = textContent;
  return link;
}

const createUl = (items) => {
  const ul = document.createElement('ul');
  let li;
  for (const item of items) {
    li = document.createElement('li');
    li.textContent = item;
    ul.appendChild(li);
  }
  return ul;
}

const items = ['foo', 'bar', 'baz'];
const ul = createUl(items);
const googleLink = createLink('Google', '[http://www.google.com'](http://www.google.com%27));
const yahooLink = createLink('Google', '[http://www.yahoo.com'](http://www.yahoo.com%27));
document.body.appendChild(ul);
document.body.appendChild(googleLink);
document.body.appendChild(yahooLink);

We should write it the second way because we divide our code into functions that always return the same thing given the same input.

Also, they can be called anywhere. So if we want another link we can call createLink again for example.

We also return the element, so we can attach it outside the functions, which means we can create elements on the fly without attaching them right away.

Conclusion

When we deal with client-side JavaScript code, we should avoid DOM manipulation as much as possible for increased performance and less code complexity.

Also, lots of JavaScript shortcuts make sense, like using literals whenever they’re available instead of the constructor.

Finally, we should divide code into functions that each does a single task to keep things easy to read and maintain. It also makes functions easier to reuse since they don’t have conflicting functionality.