undefined

The undefined primitive type expresses that something hasn't been defined yet or remains undefined. Like null, it is a type with only one value (undefined). Unlike null, an undefined value should not be explicitly set, but may be returned by the language when something does not have a value:

const coffee = {
type: 'Flat White',
shots: 2
};

coffee.name; // => undefined
coffee.type; // => "Flat White"

Undefined is best thought of as the absence of something. If you ever find yourself wishing to explicitly set something to undefined, you should probably reach for null instead. 

It's important to distinguish between the concepts of undefined and not even declared. In JavaScript, if you try to evaluate an identifier that does not exist within your scope, you will get a ReferenceError:

thisDoesNotExist; // !! ReferenceError: thisDoesNotExist is not defined

However, as you've already seen, if you try to evaluate a property of an object and the property does not exist, you will get no such error. Instead, it will evaluate to undefined:

const obj = {};
obj.foo; // => undefined

However, if you try to access a property under the non-existent foo property, you'll receive a TypeError complaining that it cannot read a property that has an undefined value:

obj.foo.baz; // !! TypeError: Cannot read property 'baz' of undefined

This behavior is an extension of the fact that seeking to access any property on an undefined or null value will always throw such a TypeError:

(undefined).foo;  // !! TypeError: Cannot read property 'foo' of undefined

Curiously, the undefined value, unlike null, is not a literal, but is a globally available value provided by the language. Overwriting this global value is not possible in ECMAScript 2015 onward, but it is still possible to define your own value for the undefined identifier in local (non-global) scopes:

undefined; // => undefined

function weird() {
let undefined = 1;
undefined; // => 1
}

This is an anti-pattern as it can create very awkward and unexpected results. The accidental setting of undefined in a scope higher than your scope can mean that, if you were to rely on the value directly, you may end up referring to a value other than undefined. This lack of trust in the undefined value has historically meant that people have found other ways to forcefully make undefined available in their scope. For example, declaring a variable but not assigning it will always result in its value being undefined:

function scopeWithReliableUndefined() {
let undefined;
undefined; // => undefined
}

You can also use JavaScript's void operator on any value that will always return the real undefined value:

void 0;         // => undefined
void null; // => undefined
void undefined; // => undefined

Explicitly setting undefined within your scope means that you can safely refer to your undefined value without worrying that it has been compromised. Fortunately, however, you can avoid the pain of having to worry about this risk by using the typeof operator:

if (typeof myValue === 'undefined') { ... }

This will not throw a ReferenceError even if myValue does not exist. The typeof operator, as we've discovered with null, is a bit of a fair-weather friend as we can't always rely on it, but it is nonetheless very useful when explicitly checking for undefined.

Another way to avoid the risk of undefined is to enforce its correct usage within your code base by using a linting tool. We'll cover linting tools in Chapter 15Tools for Cleaner Code.

In summary, undefined can be used cleanly if you remember the following two points:

  • Avoid directly assigning undefined to a variable; you should use null instead
  • Always check for undefined explicitly, preferring the typeof operator

This concludes our exploration of primitive types in JavaScript. Now, we'll move on to non-primitives, that is, objects.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.218.230