Categories
JavaScript Best Practices

JavaScript Antipatterns — Functions

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at some antipatterns that we should avoid when we’re defining and using functions.

Names and Hoisting

Function declarations ae hoisted. This means that they’re pulled up to the top.

Their definition can’t be assigned to variables or properties or appear in invocations as parameters.

For instance, if they’re at the top-level:

function foo() {}

Then they’re available everywhere.

It the function is inside a function as follows:

function local() {
  // local scope
  function bar() {}
  return bar;
}

Then bar is returned when we called local , so that it’s not available everywhere.

Function’s name Property

To get the name of the function, we can use the name property of the function.

For instance, if we write:

function foo() {}

Then we can check the name as follows:

console.log(foo.name);

However, if we have the following:

let bar = function foo() {}

Then bar.name is still 'foo' .

This also works with arrow functions. So if we write:

let bar = () => {}

Then bar.name is still 'bar' .

Function Hoisting

Functions declarations are hoisted.

So we can define a function declaration anywhere and call it.

For instance, we can write:

foo();

function foo() {
  console.log('foo');
}

And 'foo' will be logged.

However, if we the following function expression:

bar();

var bar = function() {
  alert('bar');
};

Then we get bar is not a function error.

This is one reason we shouldn’t use var to declare variables.

var only hoists the variable, but not its value.

This is a major source of confusion for many people looking at JavaScript apps.

Callback Pattern

Functions are objects, so we can pass them into other functions as parameters.

For instance, we can write the following:

function foo(callback) {
  // ...
  callback();
  // ...
}

or:

const foo = (callback) => {
  // ...
  callback();
  // ...
}

callback is a function that can be called.

A callback can be a standalone function or a method of an object.

For instance, we can write:

const person = {
  name: 'joe',
  getName() {
    console.log(this.name);
  }
};

const foo = (callback) => {
  callback();
}

foo(person.getName);

We don’t get 'joe' logged.

Instead, we should write:

const person = {
  name: 'joe',
  getName() {
    console.log(this.name);
  }
};

const foo = (callback, obj) => {
  callback.call(obj);
}

foo(person.getName, person);

We need the call method to call the callback with the object that we want to set as the value of this inside the callback function.

call lets us change the value of this and call it with arguments that we want.

We may also want to check if the value passed in is actually a function.

To do that, we can use the typeof operator to do that.

For instance, we can write:

const foo = (callback, obj) => {
  if (typeof callback === "function") {
    callback.call(obj);
  }
}

This will make sure that callback is actually a function before calling it.

Asynchronous Event Listeners

Async event listeners are used everywhere in JavaScript.

Therefore, we should be aware of them.

The callbacks passed in there are called in an indeterminate amount of time. For instance, when an event is triggered.

For instance, we can write:

document.addEventListener("click", console.log, false);

to log the clicks events triggered on the page.

We can replace 'click' with other events like 'keypress' , 'mouseover' , 'mousemove' , and many others.

Timeouts

Async callbacks are also used by timers.

So setTimeout and setInterval all take callbacks which let us call them in when the given amount of time in milliseconds has elapsed.

For instance, we can write:

setTimeout(() => console.log('time is up'), 500);

And we get that string logged in 500ms.

For setInterval , we can do the same thing:

setInterval(() => console.log('time is up'), 500);

Then the string will be logged every half a second.

Conclusion

Functions are first-class in JavaScript, which means that they’re treated like any object.

They have properties and can be passed into other functions as arguments.

This is why we can have callbacks in JavaScript. We can pass in a function and call it inside another function.

Categories
JavaScript Best Practices

JavaScript Antipatterns — Formatting and More

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at some antipatterns that we should avoid when we format JavaScript code, naming, and white spaces.

Coding Conventions

We got to establish some coding conventions so that it’s predictable and neat.

That helps with understanding a lot better.

Indentation

It’s impossible to read code without indentation. Therefore, we should indent our code.

To do that, we should indent code inside blocks.

So we write:

const foo = () => {  
  console.log('foo')  
}

or:

for (let i = 0; i < max; i++) {  
  console.log('foo')  
}

or:

if (val === 'foo') {  
  console.log('foo')  
}

We do the same with any kind of nesting.

2 spaces is a good idea. It’s more portable than tabs as they’re the same everywhere.

A space character is always a space character, but tabs are up for interpretation.

Curly Braces

As we can see, we have curly braces in all our blocks.

We can skip them if we only have one line in the if or loop body.

But it’s not a good idea to skip them since it makes reading the blocks much easier.

Therefore, we should always use curly braces to delimit blocks.

For instance, we shouldn’t write:

for (let i = 0; i < 10; i++)  
  console.log(i);

But instead, we write:

for (let i = 0; i < 10; i++) {  
  console.log(i);  
}

Similar instead of writing the following if block:

if (i === 1)   
  console.log(i);

We write:

if (i === 1) {  
  console.log(i);  
}

Opening Brace Location

The opening brace is location is important. We may interpret the code differently in different places.

In JavaScript, it’s usually in the first line of the block.

For instance, we have:

if (i === 1) {  
  console.log(i);  
}

and:

for (let i = 0; i < 10; i++) {  
  console.log(i);  
}

and:

const foo = () => {  
  console.log('foo')  
}

where the opening brace is always on the first line.

This is especially important is we’re returning something.

For instance, the following:

const foo = () => {  
  return {  
    foo: 'foo'  
  }  
}

is different from:

const foo = () => {  
  return   
  {  
    foo: 'foo'  
  }  
}

The code above is actually the same as:

const foo = () => {  
  return;  
  {  
    foo: 'foo'  
  }  
}

So it won’t return the object.

Javascript automatically inserts semicolons when it thinks it makes sense.

Therefore, in return statements, we should have the opening curly brace in the same line as the return keyword.

White Space

Having white space also improves readability.

For instance, if we have:

x<1

That’s not too readable.

However, if we have:

x < 1

That’s a lot clearer.

Good places to put white spaces include the following locations:

  • after the semicolons for separating parts of a loop — for (let i = 0; i < 10; i++){ }
  • initializing multiple variables — let a = 1, b = 2;
  • after the comma that separate array item s— [1, 2, 3]
  • after commas in object properties — {a: 1, b: 2}
  • separating function arguments — fn(a, b, c)
  • before curly braces in functions declarations — function fn() {}
  • after the function keyword — function () {};

We should also put spaces between operands in arithmetic or comparison operations.

For instance, 1 + 2 is better than 1+2 ;

Naming Conventions

Names are also important. They’ve to descriptive and they have to be consistent.

This way we can predict what things mean and won’t have to look as hard to find out their true meaning.

Capitalizing Constructors and Classes

Constructor and class names should start with a capital.

For instance, we write:

function FooConstructor() {...}

and:

class Foo {...}

All words should start with capital as we can see.

Conclusion

We should stick to some common conventions for formatting JavaScript code.

This includes capitalizing first letters of words in constructor and class names.

Also, we should add spaces and indentation to make reading code easier.

In addition, we should always have curly braces for blocks whether they’re required or not.

Also, we should have braces in the right location so that they won’t be interpreted in ways that we don’t expect.

Categories
JavaScript Best Practices

JavaScript Antipatterns — Prototypes, Switch, and More

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at some antipatterns that we should avoid when we write JavaScript prototypes, switch statements, use of eval and parsing integers.

Built-in Prototypes

We should never mess with prototypes of built-in constructors like Array or Function .

If we need extra functionality that they don’t provide, we either make our own class or function.

Messing with built-in prototypes will bring problems with conflicts and people will be confused about why something is added to the prototype or why some methods act differently than from the docs.

Properties that we add to the prototype might also show up in loops that don’t use hasOwnProperty , which is even more confusing.

For instance, we should never write something like:

Array.prototype.foo = function(){
  //...
}

Instead, we write our own function to pass in an array:

function(arr) {
  //...
}

switch Pattern

We can use switch statements to replace a series of if and else statements.

For instance, we can write:

let val = 0,
  result = '';

switch (val) {
  case 0:
    result = "zero";
    break;
  case 1:
    result = "one";
    break;
  default:
    result = "unknown";
}

instead of:

let val = 0,
  result = '';

if (val === 0) {
  result = 'zero';
} else if (val === 1) {
  result = 'one';
} else {
  result = 'unknown';
}

It’s longer but more readable.

We’ve to remember to add break to each case statement so that once a matching case is found, it’ll stop running the other cases.

Also, we should end with a default case so that we do something even if no matching value is found.

Avoiding Implied Typecasting

To avoid implied typecasting, we should avoid using the == operator for equality comparison.

The rules are complex and confusing for type conversions.

Instead, we should use the === operator for comparison:

const foo = 0;
if (foo === false) {
  // not running because foo is 0, not false
}

The type of both operands have to be the same before comparison, so the body won’t run.

Likewise, we use the !== operator for inequality comparison.

So we write:

const foo = 0;
if (foo !== false) {
  // runs because foo is 0, not false
}

If we use the < , > , <= , or >= operators, then we’ve to convert the types of the operators to the same types explicitly to avoid confusion.

Avoiding eval()

eval should be avoided. Security issues arise from running code from strings.

It’s also very hard to debug code that is in strings.

Also, browsers can’t optimize code that is in strings.

Therefore, we should never use it.

setInterval , setTimeout , and Function constructor all take strings.

So we shouldn’t use the Function constructor to define functions.

If we call setInterval or setTimeout , the callback should be a function and not a string.

So instead of writing:

setTimeout(`console.log('hello')`)

We write:

setTimeout(() => console.log('hello'))

Number Conversions with parseInt()

We can call parseInt to get a numeric value from a string.

It takes a 2nd radix parameter to specify the base of the number.

We should include the 2nd argument so we make sure that the number string is converted properly to a number.

For instance, we should write:

const num = parseInt("16", 10);

so that we know that 16 is a decimal number.

However, we should be careful with parseInt since it’ll try to convert strings with numbers and letters.

For instance, parseInt(“1 abc”) returns 1.

We can also use the + operator to convert something to a number by writing:

const num = +"16";

On the other hand, +”1 abc” return NaN , which is probably what we want.

Conclusion

We should never add methods to built-in prototypes.

It just confuses people and it may overwrite existing methods, which is no good.

switch statements may be good alternatives to if and else.

We shouldn’t use eval or related methods like the Function constructor or passing strings to setTimeout or setInterval .

Categories
React

React Antipatterns to Avoid

React is the most used front end library for building modern, interactive front end web apps. It can also be used to build mobile apps.

In this article, we’ll look at some React antipatterns that we should avoid.

bind() and Functions in Components

When we call bind in React class components, we shouldn’t call them repeatedly in our props. Instead, we should call bind in the constructor so that we don’t have to call bind on the fly when we pass in our class component methods as props.

For instance, we can write the following code to do that:

import React from "react";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: ""
    };
    this.updateValue = this.updateValue.bind(this);
  }
  updateValue(e) {
    this.setState({
      name: e.target.value
    });
  }
  render() {
    return (
      <>
        <form>
          <input onChange={this.updateValue} value={this.state.name} />
        </form>
        <p>{this.state.name}</p>
      </>
    );
  }
}

In the code above, we have an input which calls the updateValue method when the input value is changed.

We have:

this.updateValue = this.updateValue.bind(this);

in the constructor so that it’ll always bind the component as the value of this .

Now we don’t have to do it again repeatedly everywhere. Also, we won’t be creating a new function every time the onChange event is emitted since we didn’t call bind , which returns a new function.

The performance hit from creating methods on the fly when our app grows large is going to be noticeable in some situations.

So instead of writing:

<input onChange={this.updateValue.bind(this)} value={this.state.name} />

we write what we have above.

Missing key Prop or Using indexes in key Prop

The key prop is important when we render lists, even though it may seem like it’s doing nothing sometimes. The unique key prop value lets React identify list items correctly.

key prop values are used to match the children in the original tree with the children in the subsequent tree.

Therefore, using the array’s index is also bad because it may lead React to render the wrong data.

It’s best to use unique IDs as the value of the key prop. We can create an array with unique IDs that we can use as keys as follows:

import React from "react";
import { v4 as uuidv4 } from "uuid";

const arr = [
  {
    fruit: "apple",
    id: uuidv4()
  },
  {
    fruit: "orange",
    id: uuidv4()
  },
  {
    fruit: "grape",
    id: uuidv4()
  }
];

export default function App() {
  return (
    <div className="App">
      {arr.map(a => (
        <p key={arr.id}>{a.fruit}</p>
      ))}
    </div>
  );
}

In the code above, we used the uuid package to create a unique UUID to use as the value of the key prop. This way, there’s almost no chance of collision of unique IDs.

This way, IDs are unique and predictable, and we don’t have to think of a way to generate the unique ID for each item ourselves.

Components Name

Component names have to begin with a capital letter. For instance, if we write the following code, we’ll get an error from React:

import React from "react";

const foo = () => <p>foo</p>;

export default function App() {
  return (
    <div className="App">
      <foo />
    </div>
  );
}

If we run the code above, React will give us the error ‘<foo> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.’

Therefore, we should instead name our components always with a capital letter to start:

import React from "react";

const Foo = () => <p>foo</p>;

export default function App() {
  return (
    <div className="App">
      <Foo />
    </div>
  );
}

Then we’ll see ‘foo’ displayed on the screen as we expected.

Photo by chatnarin pramnapan on Unsplash

Close Every Element

All component tags have to have a closing tag or a slash in the end for self-closing components. For instance, if we have:

import React from "react";

const Foo = () => <p>foo</p>;

export default function App() {
  return (
    <div className="App">
      <Foo>
    </div>
  );
}

We’ll get a syntax error because we’re missing a closing slash or tag in:

<Foo>

To make the app run, we write instead:

import React from "react";

const Foo = () => <p>foo</p>;

export default function App() {
  return (
    <div className="App">
      <Foo />
    </div>
  );
}

<Foo></Foo> also works.

Conclusion

It’s easy to make mistakes when we’re writing React components. We have to remember to capitalize the first letter of component names. Also, we have to close the tags of the component or add a closing slash for self-closing components.

Also, we should call bind first so that we don’t have to call it repeatedly to bind to the right value of this in class-based components.

Categories
JavaScript Best Practices

List of JavaScript Antipatterns

JavaScript is a language that’s used a lot in many places including web development and more.

Like any other language, it’s easy to commit antipatterns when we’re programming with JavaScript.

In this article, we’ll look at some antipatterns we should avoid.

Polluting the Global Namespace

Polluting the global namespace is bad because it’ll lead to name collisions,

Without strict mode, it’s also easy to accidentally declare global variables.

For instance, without strict mode, we can write something like:

x = 1;

which is the same as:

window.x = 1;

window is the global object in browser JavaScript.

Global variables are accessible everywhere so that we can change their values anywhere.

This isn’t good because we don’t want to run into bugs because of accidental reassignments.

An example of accidental assignment would be something like:

function foo() {
  return 1;
}

function bar() {
  for (i = 0; i < 10; i++) {}
}

i = foo();
bar();

where we assign i in many places. i was assigned in multiple places in a confusing way.

Extending the Object prototype

We should never add any new properties to Object.prototype .

This is because it’s global. It’ll affect other objects since almost all objects extend the Object.prototype .

If everyone does this, it’s very easy for people to overwrite the Object ‘s prototype with their own code.

Instead, we should use Object.defineProperty to define properties with property descriptors that we want to set if we want control over our object’s properties.

Using Multiple var Declarations Instead of One

Multiple var declarations are less readable and slightly slower, so instead of writing:

var a = 1;
var b = 2;
var c = 3;

we should write:

var a = 1,
  b = 2,
  c = 3;

Using new Array(), new Object(), new String(), and new Number()

There’s no reason to use the Array constructor. It’s confusing since the single argument versions and the multiple arguments version do different things.

The single-argument version takes a number and returns an array with the length set by the argument.

The multiple argument version takes the content of the array as arguments. We can pass in as many as we want to populate our new array.

For instance:

const arr = new Array(5);

returns an array with 5 empty slots.

On the other hand,

const arr = new Array(1, 2, 3);

returns [1, 2, 3] .

Instead, we should use array literals to make things simple for us:

const arr = `[1, 2, 3];`

It’s simpler and less confusing.

Likewise, new Object() is just extra writing. We have to define the properties on different lines as follows if we want to add properties:

const obj = new Object();
obj.a = 1;
obj.b = 2;
obj.c = 3;

Then obj ‘s value is {a: 1, b: 2, c: 3} .

That’s just extra writing. Instead, we should use object literals instead:

const obj = {
  a: 1,
  b: 2,
  c: 3
}

It’s much less typing and also pretty clear.

The problem with new String() and new Number() are that they give us the type 'object' . There’s no reason that we need strings and numbers to have the type 'object' .

We’ve to call valueOf() if we want to convert them back to primitive values.

Instead, we should use the factory functions String() and Number() to convert things to string or number.

For instance, instead of writing:

const str = new String(1);

We write:

const str = String(1);

Also, instead of writing:

const num = new Number('1');

We write:

const num = Number('1');

Relying on the Ordering of Iterator Functions Like .map and .filter

We shouldn’t rely on the iteration order of forEach, map and filter .

The iteration also doesn’t work if we pass in async function as the filter.

If we need to iterate through things in a specific order, then we should sort them the way we want before iteration.

Also, if we want to use the index of the entry, we can pass it into the callback so that we get index reliably.

For instance, we can write the following to get the index and the original array in the callback as follows:

const nums = [1, 2, 3];
nums.forEach((num, index, nums) => {
  if (index === nums.length - 1) {
    console.log('end');
  }
})

This is much better than:

const nums = [1, 2, 3];
let count = 0;
nums.forEach((num) => {
  if (count === nums.length - 1) {
    console.log('end');
  }
  count++
})

Not only the second example more complex, but it’s less reliable since we may accidentally change the count elsewhere.

On the other hand, accessing the index and original array from the callback parameters means there’s no chance of modifying count outside.

This also applies to map and filter .

Conclusion

We can should avoid antipatterns like global variables, unnecessary use of constructors, and extending the Object ‘s prototype.

Also, since we can access the numbers from the parameters, we shouldn’t create variables to track the index on the outside.