Avoiding “Undefined is Not a Function” with Constants

How many times have you come across JavaScript’s “Undefined is not a function”? Too many. JavaScript is known for being so flexible that it’s easy to create unintentional bugs.

One way we can add structure to JavaScript code is to make a habit of using constants. Constants pair well in JavaScript with JS’s powerful object data structure, and they can prevent all kinds of problems, such as:

  • “What key do I need to access to get the property I want?”
  • “Where can I go to see all the keys that can be accessed on an object?”
  • “Undefined is not a function” (sometimes)

Working without Constants

Defining your object’s keys as a constant helps to avoid accessing values that don’t exist, and getting errors as a result. For example, let’s say we have an application with translations. Translations are generated by a third party and provided for us in this form:

sell = {eng: sell, esp: vender}

We also have a

getLang()

function that returns the language as “ENGLISH” or “SPANISH.”

With this, we might write the following code for translating our word:


if (getLang() === ‘ENGLISH’) {
  return sell.eng;
}
else if (getLang() === ’SPANISH’) {
  return sell.esp;
}

For now, this is working. But what happens if getLang() is updated and starts returning “English” instead of “ENGLISH”? Now, every comparison instance of getLang() needs to be changed from the all upper case.

How to Use Constants in JavaScript

Here’s how constants can work in our favor.

1. Use constants to store values that will be used repeatedly.


const Languages = {english: “ENGLISH”, spanish: “SPANISH”}
if (getLang() === Languages.english) {
  return sell.eng;
}
else if (getLang() === Languages.spanish) {
  return sell.esp;
}

2. Use constants as keys that are used repeatedly.

We no longer have any string literals in our business logic, which is good. However, ‘eng’ and ‘esp’ aren’t the most descriptive key names. Since our translations come from a third party (and even if they didn’t), there’s no guarantee that  ‘eng’ and  ‘esp’ will be the relevant keys forever.

Consider, if any of the values on sell are functions, and the keys change, we will get an “undefined is not a function” error.

A change of accessing every .eng and .esp could be expensive, but it will be easy if we use a constant. As a bonus, we can use whole words to make it more readable.


const Translated= {english: “eng”, spanish: “esp”};
if (getLang() === Languages.english) {
  return sell[Translated.english];
}
else if (getLang() === Languages.spanish) {
  return sell[Translated.spanish];
}

With this approach, constants also add some documentation to the code about what keys an object has and doesn’t have, as well as how those keys are spelled and cased.

3. Use constants to factor out control statements.

In the example above, doesn’t “Translated.english” mean something similar to “Languages.english”? And wouldn’t it be nice to get rid of that if block altogether? We can use constants to make that happen. Consider the example below: (I’m using ECMAScript 2015 syntax to define keys using other objects).


const BrowserLanguages = {english: ‘ENGLISH’, spanish: “SPANISH”}

 

const TranslationKeys = {

 

[BrowserLanguages.english]: ‘eng’,

 

[BrowserLanguages.spanish]: ‘esp’

 

}

Once these constants are set up, we can easily access the language we want like this:


return sell[TranslationKeys[getLang()]]

And if either getLang() or our translator change their format, the only thing that needs to be updated is our constants.

Advantages of Constants in JavaScript

Why constraints?

  • Language names can be updated, maintained, and re-used independently.
  • Language names will always refer to a human readable key, even if the value isn’t.
  • Language names are strongly tied to our translator keys.
  • Bonus:
    sell[Languages[lang]]

    is a safe, optimized, one-line representation of our logic.

I won’t say an “undefined” error is impossible with this approach, but it’s much less likely to be an issue.

In my experience working in active codebases, properties and keys can change all the time. Using constants is one technique I use to make it easy to absorb these kinds of changes.