Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — New Features

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at how to add polyfills and language features to our app to add features that aren’t built into the browsers we’re targeting.

Polyfills

Polyfills are important for making our client-side JavaScript code robust. Polyfills are scripts that add features that browsers may not otherwise support.

It implements particular JavaScript APIs that browsers may not support yet. Part of the JavaScript standard library that isn’t available in the browser that we’re targeting also need polyfills.

They’re just libraries that we add just like any other library. For instance, some API like the Fetch API or the Network Information API may not be available yet.

However, we want to use them so that we can make HTTP requests adding an extra library or watching for network connection type changes respectively.

Then we need to add polyfills for them. If we need to add support for the Fetch API for browsers that don’t support them, then we need to install a Fetch polyfill by following the instructions at https://github.com/github/fetch.

We should only use the polyfill version if the native version isn’t available as the native version is faster since it’s built into the browser rather than added on the fly.

Likewise, there’s a Network Information API polyfill at https://www.npmjs.com/package/network-information-api-polyfill so that we can use the Network Information API in older browsers.

New features added to browser global objects like Array may also need a polyfill for them to work.

For instance, if we need to use the array instance’s find method in IE, then we need to add a polyfill for it. The script for it is available at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find.

There’s also which is a one-stop-shop for all the polyfills we need to add new native features to browsers that may not be available.

Some features like audio and video stream access do not have polyfills as they require native hardware support. Therefore, those features would need to be disabled in our app if our app supports an old browser that doesn’t have this feature built-in.

The Babel Compiler

To add new JavaScript features that many browsers don’t support out of the box, we would need to add a compile step to our browser so that when we use new JavaScript language features in our app, that it would compile down into code that’s compatible with the browsers that we target.

We can use the Babel compiler to do that. It supports all current features in addition to ones that are experimental.

It’s better to stick with the current ones in a production app as the experimental ones are subject to breaking changes.

Changes that are to become standard are in Stage 4, which means that they’ll be added as a standard for sure into the JavaScript language.

Useful JavaScript features that are to become standard are ones like the optional chaining operator.

For instance, if we have a plain JavaScript that’s compiled with Parcel, which comes with Babel already, then we can add the @babel/plugin-proposal-optional-chaining Babel plugin by running:

npm i @babel/plugin-proposal-optional-chaining

Then in the .babelrc file of the project, we add:

{  
  "plugins": \[  
    "@babel/plugin-proposal-optional-chaining"  
  \]  
}

Then we can use it on our JavaScript code adding:

const foo = {};  
const x = foo?.bar?.baz;  
console.log(x);

Babel supports many more features than just the optional chaining operator. It also works with both Node.js and client-side JavaScript.

Pretty much any more JavaScript features are supported with Babel, mostly without plugins. Features like let , const , for...of , modules, maps, sets, symbols, rest and spread operators, destructuring, etc. are all supported by Babel.

We can also add Babel as a script for legacy apps or for prototyping by adding:

<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

to our HTML.

Then if we have a script tag with the type attribute set to text/babel , then we can add modern Javascript features without worrying about breaking the app in old browsers.

Conclusion

Polyfills are scripts that add support for features that may not be available in old browsers.

To add native JavaScript features that may not be available in the browser that our app is targeting, we can use the Babel compiler to add support for them. We can also add Babel plugins to support more cutting edge features.

In addition, we can compile modern JavaScript code on the fly by using the script tag version of Babel and add our own scripts with the type attribute set to text/babel to add support for it without building.

Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — More About Functions

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at good function features that JavaScript has that we should use.

Destructuring Arguments

Destructuring arguments should be used to take parts of an argument and reference that. This way, we can reference a part of an argument as a variable rather than having to use the path of the property or array to access it.

Also, there are much fewer chances for errors since some properties may be non-existent, null , or undefined , and they’ll throw errors if we access them with the path.

We don’t have to worry about that since we can set default values for properties with the destructuring syntax.

For instance, we can do that with the rest operator and arrays as follows:

const f = (a, b, ...[d, e]) => {
  console.log(d, e);
}

Then when we call it as follows:

f(1, 2, 3, 4, 5);

We get that d is 3 and e is 4.

Also, we can use the destructuring syntax on object arguments. For instance, we can write the following code to use that:

const f = ({
  foo: {
    bar
  }
}) => {
  console.log(bar);
}

In the code above, we have the following function signature:

({
  foo: {
    bar
  }
})

This code takes the foo.bar property’s value and assigns it to the bar variable.

Then when we call it as follows:

f({
  foo: {
    bar: 1
  }
});

We get that bar in f is 1.

The good thing about the destructuring syntax is that it won’t throw errors when we try to destructure a property that doesn’t exist if we set default values for them

For instance, if we have the following function:

const f = ({
  foo: {
    bar
  } = {}
}) => {
  console.log(bar);
}

Then when we call it as follows:

f({});

We’ll get undefined instead of an error since we set the foo property to an empty object.

Likewise, we can set default values for destructured array entries as we wish. For instance, we can write:

const f = (a, b, ...[d, e = 4]) => {
  console.log(d, e);
}

Then we call it as follows:

f(1, 25);

We get that d is undefined and e is 4 since we set e ‘s default value to 4.

As we can see, there’s less chance for committing errors and also it’s clearer since we can see the structure of the arguments.

Minimize the Use of Traditional Functions

The use of traditional functions should be minimized. Not only we need to type the function keyword every time.

Also, with traditional functions, we have to worry about the value of this and arguments . We also have to think if it’s hoisted or not.

Hoisted functions, which are function declarations, can be referenced anywhere in a script, but function expressions can’t be referenced until they’re defined.

Moreover, we don’t have to worry about how to call , bind , and apply if we don’t use traditional functions.

These things are always confusing.

With the class syntax for constructor functions, there’s also no reason to use them to create constructor functions.

This is because the class syntax tells us when we have errors in our class declarations.

For instance, if we have the following JavaScript class hierarchy:

class Foo {
  constructor(foo) {
    this.foo = foo;
  }
}
class Bar extends Foo {
  constructor(foo) {
    super(foo);
  }
}
const bar = new Bar();

Then when we forgot to call super in the constructor of Bar , we’ll get an error since we have constructor in the Foo class that takes an argument.

We want these errors to be shown so we can fix them before the code goes to production.

This is in contrast to what we have before we have the class syntax:

function Foo(foo){
  this.foo = foo;
}

function Bar(foo){
  Foo.call(this, foo);
}

Bar.prototype.constructor = Foo;
const bar = new Bar('foo')

This does the same thing as the class syntax, but we get no errors if we miss the call and constructor .

Conclusion

We should use the destructuring syntax so that we can see the structure of our arguments more clearly and selectively use parts of the arguments in our functions.

Also, we shouldn’t use traditional functions anymore in most cases since they are more confusing and complex and harder to understand.

Categories
JavaScript Best Practices

JavaScript Best Practices — Variable Names to Avoid

JavaScript is a very forgiving language. It’s easy to write code that runs but has issues in it.

In this article, we’ll look at the kind of names we want to avoid for JavaScript variables.

Avoid Misleading Names or Abbreviations

Is a name is misleading, then we should correct them.

So a variable shouldn’t have double meanings. The variable age should hold the age of something for example.

Avoid Names with Similar Meanings

If names have a similar meaning, then we should avoid them. Something like employeeCode and employeeNum can easily confuse us.

If we have to fix problems, then we’ll run into issues with the confusing that these names bring.

Avoid Variables with Different Meanings but Similar Names

Variable names with different meanings but look similar are also no good.

Something like employeeRecs and employeeReps aren’t good.

The difference in their spelling is only by 1 letter, which can easily be missed.

Avoid Names that Sound Similar

Name that sound similar may also confuse us. Therefore, we should avoid them.

For instance, wrap and rap sound the same, so we can easily confuse the 2 names.

Avoid Numerals in Names

Since numbers are already used for array indexes, we shouldn’t use them for variables names as a suffix.

If we have foo1 and foo2 , then either they aren’t differentiated enough in their name or they should be in the same array.

Avoid Misspelled Words in Names

Misspelled words can easily throw people off, so we should avoid them to avoid any problems with them in the future.

If we have variable names that are misspelled, we should correct them before we go further.

Don’t Differentiate Variable Names Solely by Capitalization

Only differentiating names by capitalization doesn’t differentiate them much.

It’s hard for us to look at names that only differ by this little.

Avoid Multiple Natural Languages

If everyone uses English in the team, then all the names should be in English.

Otherwise, having names in different languages that not everyone may understand will bring problems if they have to read the code.

Make sure everyone knows the language before we name things.

Avoid the Names of Standard Constructors, Variables, and Functions

We should avoid any keywords that are used by the JavaScript language or its standard library.

For instance, we shouldn’t name things with words like, undefined , Array , and things like that.

Don’t Use Names that are Totally Unrelated to What the Variables Represent

If a variable is named for something, then we should make sure that it holds the value that’s given by the name.

So numApples should hold the number of apples for example.

Avoid Names Containing Hard-to-Read Characters

If they’re hard to read characters, then we should avoid them so that everyone can read them easier.

Characters like l and I shouldn’t be used together for example as they both look like a vertical line.

Conclusion

When naming variables, we should make them as easy to understand as possible.

This means that misspellings should be fixed. Names that have a similar meaning, sound, or appearance should also be changed.

Also, we’ve to make sure that everyone knows the language that the variable name is written so that everyone can understand them.

Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Functions

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at the best ways to write functions that are less likely to cause errors.

Use Arrow Functions as Much as Possible

JavaScript arrow functions are great. They let us write functions in a shorter and cleaner way.

Also, we don’t have to worry about the value of this inside a function. The value of this is always a pain to think about since it takes on a different value in different locations.

Also, we don’t have to worry about the value of the arguments object since it doesn’t bind to it.

Therefore, we should use it as much as possible. For instance, we can write something like:

const arr = [1, 2, 3];
const arr2 = arr.map(x => x * 2);

In the code above, x => x * 2 is our arrow function. As we can see, it’s short, and since it’s one line, we don’t have to write return explicitly to return x * 2 . One line functions are much cleaner.

We don’t have to write out the function keyword all the time to declare a function.

Also, we also can’t call bind , call , or apply with it, so that we don’t have to worry about those methods either. This is another reason that we should use arrow functions as much as possible since there are fewer risks for errors from calling these methods.

They also can’t be used as constructor functions so we can’t use the new keyword with them. This is good because don’t want to use constructor functions anymore with the new class syntax.

Another good thing is that they don’t have a prototype property since they can’t be used as constructor functions. That’s another place that we can’t commit errors in the code.

Also, there’re no function declarations with arrow functions, so they can’t be hoisted. This means that they can only be referenced after they’re defined. This removes a lot of confusion with where we can call or reference an arrow function.

They can be used as top-level functions, as methods of objects, or be nested anywhere.

For instance, we can write:

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

So we can call the obj.foo method to log 'foo' .

We can also write:

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

to nest foo inside bar .

Therefore, they’re good to use in places other than for constructor functions. We don’t really want to use constructor functions anyways since there’s the class syntax which actively checks for errors in our code instead of letting errors slide with constructor functions.

This is even more critical when we inherit from another constructor function.

Using the Rest Operator for Getting Long List of Arguments

The rest operator should always be used instead of the arguments object if we have a long list of arguments passed into our function.

It’s denoted by ... in the function signature and it returns the extra arguments that aren’t set as the value of the named parameters as an array.

They work with arrow functions and traditional functions. For instance, we can use the rest operator as follows:

function f(a, b, ...args) {
  console.log(args);
}

When we call f(1, 2, 3, 4, 5); then args is [3, 4, 5] since 1 is set to a and 2 is set to b .

We can also use them with arrow functions:

const f = (a, b, ...args) => {
  console.log(args);
}

Then we get the same result as before.

This way, we don’t have to use the arguments object to get the arguments, or use call or apply to call a function with extra arguments.

Like any array, we can use the destructuring syntax with it. For instance, we can write:

const f = (a, b, ...[d, e]) => {
  console.log(d, e);
}

Then when we call it as follows:

f(1, 2, 3, 4, 5);

We get that d is 3, e is 4. Note that we don’t have to destructure all the arguments that are passed in.

This is much better than the arguments object since it’s an array rather than an array-like object and we can use destructuring syntax with it.

Conclusion

Arrow functions should be used in our JavaScript code except when we need to create constructors or reference this in an object method.

The rest operator is the best way to get the arguments of a function that isn’t assigned to named parameters.

Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Checking for Feature Availability

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at various ways to check if a library is available in the browser you target before using it.

Modernizr

The Modernizr library lets us detect if a feature is available before using it. We can add it to our app with a script tag as follows:

<script
      type="text/javascript"
     src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.min.js"
    ></script>

Then we can add code like the following to test if a library is available as follows:

window.Modernizr.addTest("hasJquery", "jQuery" in window);

In the code above, we check if jQuery is available by calling Modernizr’s addTest method to check if the 'jQuery' property is in the global window object to see if jQuery has been added.

Then we can check with the Modernizr.on method as follows:

window.Modernizr.on("hasjquery", result => {
  console.log(result);
});

result will be true if jQuery exists and false otherwise. Note that most methods are only available in a custom build, which can be created by going to https://modernizr.com/download?setclasses.

Then every time the page loads, the feature check will be run to check if the hasjquery feature returns true .

We can also add checks for multiple features as follows:

const detects = {
  'hasjquery': 'jQuery' in window,
  foo() {
    return true
  }
}
Modernizr.addTest(detects);

In the code above, we have the hasjquery check and the foo check. Note that the check can be a function in addition to a boolean.

If it’s a function, then it should return a boolean.

Modernizr also has the atRule method to check is a CSS @ rule is available in the current browser.

For instance, we can use it as follows:

const keyframes = Modernizr.atRule('@keyframes');

If the @keyframes is available in the current browser, the keyframes would have the value '@keyframes' . Otherwise, it would have the string value 'false' .

The Modernizr _domPrefixes method gets all the CSS prefixes that are supported by a browser.

For instance, the Modernizr._domPrefixes property may return something like the following:

["moz", "o", "ms", "webkit"]

This means that we can set properties with these prefixes like for flexbox with -moz-flex , -ms-flexbox , and -webkit-box for setting the flexbox property within a browser.

ms is for Internet Explorer, moz is for Firefox, o is for Opera, and webkit for Webkit based browsers like Safari.

We can use Modernizr’s hasEvent method to check if an event is available in a browser. It takes 2 arguments. The first is the string for an event name, and the second is the object which the event listener can be attached to.

The 2nd argument is optional.

For instance, we can write the following code to use the hasEvent method:

console.log(Modernizr.hasEvent('devicelight', window))
console.log(Modernizr.hasEvent('blur', window))

In the code above, we check if the devicelight event handler can be attached to window and listened to in the browser.

Also, we checked if the blur event can be listened to in the browser on the window object.

They both should return boolean. For desktop browsers, the first one should return false , but the second one should return true since it’s available for all browsers.

The Modernizr mq method lets us programmatically check if the current browser window state matches the media query.

It takes one argument, which is the string with the CSS for the media query that we want to check.

For instance, we can do that as follows:

const query = Modernizr.mq('(min-width: 300px)');

In the code above, we check if the screen is 300px or wider. If it is, then it returns true . Otherwise, it returns false .

Modernizr’s prefixed method takes a string CSS value in the DOM style in camelCase and returns the version of that property that the browser actually supports.

This helps us determine what kind of prefix we need in our CSS to properly style our app in the current browser.

For instance, we can use it as follows:

const boxSizing = Modernizr.prefixed('boxSizing');

In the code above, we check if any prefix is needed for the box-sizing CSS property. If we do, then it’ll return the prefix with the property for the string. Otherwise, it’ll just return the string we passed in.

Conclusion

We can use Modernizr to detect various features that are available in the current browser. They include screen sizes, objects that are loaded in the browser, and CSS prefixes.

At rules can also be checked if they’re available.