What reason is there to use null instead of undefined in JavaScript?

Viewed 57679

I've been writing JavaScript for quite a long time now, and I have never had a reason to use null. It seems that undefined is always preferable and serves the same purpose programmatically. What are some practical reasons to use null instead of undefined?

19 Answers

At the end of the day, because both null and undefined coerce to the same value (Boolean(undefined) === false && Boolean(null) === false), you can technically use either to get the job done. However, there is right way, IMO.

  1. Leave the usage of undefined to the JavaScript compiler.

    undefined is used to describe variables that do not point to a reference. It is something that the JS compiler will take care for you. At compile time the JS engine will set the value of all hoisted variables to undefined. As the engine steps through the code and values becomes available the engine will assign respective values to respective variables. For those variables for whom it did not find values, the variables would continue to maintain a reference to the primitive undefined.

  2. Only use null if you explicitly want to denote the value of a variable as having "no value".

    As @com2gz states: null is used to define something programmatically empty. undefined is meant to say that the reference is not existing. A null value has a defined reference to "nothing". If you are calling a non-existing property of an object, then you will get undefined. If I would make that property intentionally empty, then it must be null so you know that it's on purpose.

TLDR; Don't use the undefined primitive. It's a value that the JS compiler will automatically set for you when you declare variables without assignment or if you try to access properties of objects for which there is no reference. On the other hand, use null if and only if you intentionally want a variable to have "no value".

I never explicitly set anything to undefined (and I haven't come across this in the many codebases I've interacted with). Also, I rarely use null. The only times I use null is when I want to denote the value of an argument to a function as having no value, i.e.,:

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

printArguments(null, " hello") // logs: null hello

A few have said that it is ok to initialise objects to null. I just wanted to point out that destructuring argument defaults don't work with null. For example:

const test = ({ name } = {}) => {
  console.log(name)
}

test() // logs undefined
test(null) // throws error

This requires performing null checks prior to calling the function which may happen often.

In JavaScript, the value null represents the intentional absence of any object value. null expresses a lack of identification, indicating that a variable points to no object.

The global undefined property represents the primitive value undefined. undefined is a primitive value automatically assigned to variables. undefined is meant to say that the reference is not existing.

Just wanna add that with usage of certain javascript libraries, null and undefined can have unintended consequences.

For example, lodash's get function, which accepts a default value as a 3rd argument:

const user = {
  address: {
    block: null,
    unit: undefined,
  }
}
console.log(_.get(user, 'address.block', 'Default Value')) // prints null
console.log(_.get(user, 'address.unit', 'Default Value')) // prints 'Default Value'
console.log(_.get(user, 'address.postalCode', 'Default Value')) // prints 'Default Value'

Another example: If you use defaultProps in React, if a property is passed null, default props are not used because null is interpreted as a defined value. e.g.

class MyComponent extends React.Component {
   static defaultProps = {
      callback: () => {console.log('COMPONENT MOUNTED')},
   }
   componentDidMount() {
      this.props.callback();
   }
}
//in some other component
<MyComponent />   // Console WILL print "COMPONENT MOUNTED"
<MyComponent callback={null}/>   // Console will NOT print "COMPONENT MOUNTED"
<MyComponent callback={undefined}/>   // Console WILL print "COMPONENT MOUNTED"

Unknown variable: undefined.

Known variable yet no value: null.

  1. You receive an object from a server, server_object.
  2. You reference server_object.errj. It tells you it’s undefined. That means it doesn’t know what that is.
  3. Now you reference server_object.err. It tells you it’s null. That means you’re referencing a correct variable but it’s empty; therefore no error.

The problem is when you declare a variable name without a value (var hello) js declares that as undefined: this variable doesn’t exist; whereas programmers mostly mean: “I’ve not given it a value yet”, the definition of null.

So the default behavior of a programmer—declaring a variable without a value as nothing—is at odds with js—declaring it as not existing. And besides, !undefined and !null are both true so most programmers treat them as equivalent.

You could of course ensure you always do var hello = null but most won’t litter their code as such to ensure type sanity in a deliberately loosely-typed language, when they and the ! operator treat both undefined and null as equivalent.

There are already some good answers here but not the one that I was looking for. null and undefined both "technically" do the same thing in terms of both being falsy, but when I read through code and I see a "null" then I'm expecting that it's a user defined null, something was explicitly set to contain no value, if I read through code and see "undefined" then I assume that it's code that was never initialized or assigned by anything. In this way code can communicate to you whether something was caused by uninitialized stuff or null values. Because of that you really shouldn't assign "undefined" manually to something otherwise it messes with the way you (or another developer) can read code. If another developer sees "undefined" they're not going to intuitively assume it's you who made it undefined, they're going to assume it's not been initialized when in fact it was. For me this is the biggest deal, when I read code I want to see what it's telling me, I don't want to guess and figure out if stuff has "actually" been initialized.

Not even to mention that using them in typescript means two different things. Using:

interface Example {
    name?: string
}

Means that name can be undefined or a string, but it can't be null. If you want it null you have to explicitly use:

interface Example {
  name: string | null
}

And even then you'll be forced to initialize it at least with "null".

That's of course only true if you're using "strictNullChecks": true in tsconfig.json.

Based on a recent breakage we ran into, the example below shows why I prefer to use undefined over null, unless there is a specific reason to do otherwise:

function myfunc (myArg) {
    if (typeof myArg === 'string') {
        console.log('a', myArg);
    } else if (typeof abc === 'object') {
        console.log('b', myArg);
        if (myArg.id) {
           console.log('myArg has an id');
        } else {
           console.log('myArg has an id');
        }
    } else {
        console.log('no value');
    }
}

The following values will play nicely:

'abc'
{}
undefined
{ id: 'xyz' }

On the other hand the assumption of null and undefined being equivalent here breaks the code. The reason being is that null is of type of object, where as undefined is of type undefined. So here the code breaks because you can't test for a member on null.

I have seen a large number of cases with code of similar appearance, where null is just asking for problems:

if (typeof myvar === 'string') {
  console.log(myvar);
} else if (typeof myvar === 'object') {
  console.log(myvar.id);  
}

The fix here would be to explicitly test for null:

if (typeof myvar === 'string') {
  console.log(myvar);
} else if (myvar !== null && typeof myvar === 'object') {
  console.log(myvar.id);  
}

My attitude is to code for the weaknesses of a language and the typical behaviours of programmers of that language, hence the philosophy here of going with 'undefined' bey default.

To write simple code you need to keep complexity and variation down. When a variable or a property on an object does not have a value it is undefined , and for a value to be null you need to assign it a null value.

Undeclared vs Null

null is both an Object "type" and one of the 7 unique primitive value types called null

undefined is both a global scope property called undefined (window.undefined) and one of the 7 unique primitive value types called undefined

It is the primitive types we use as values we are interested in.

In the case of null, as a value type it means an empty value has been assigned to a variable. It may mean a variable has an empty value but it is still a value. It also initializes the variable so it exists, and is not undefined.

undefined is a special case. When you create a variable it is assigned undefined by default prior to assigning a value, and implies the variable does not exist or exists but has no value assigned. Like null it is also a primitive value type. But unlike null it means the variable does not exist. That is why its always better to check if the variable exists and has been assigned a variable using undefined before checking if the value is null or empty. undefined implies no variable or object exists in the compilation. The variable has either not been declared or declared with a missing value so not initialized. So undefined is a very good way to avoid many types of errors and supersedes null.

That is why I would not rely on "truthy" checks for true/false with null and undefined, even though they will return a false response, as undefined implies a missing feature, object, or variable, not just a true/false check. It implies something more.

Let's look at undefined first:

        //var check1;// variable doesnt even exist so not assigned to "undefined"
        var check2;// variable declared but not initialized so assigned "undefined"
        var check3 = 'hello world';// variable has a value so not undefined

        console.log('What is undefined?');

        //console.log(check1 === undefined);// ERROR! check1 does not exist yet so not assigned undefined!
        console.log(check2 === undefined);// True
        console.log(check3 === undefined);// False
        console.log(typeof check1 === 'undefined');// True
        console.log(typeof check2 === 'undefined');// True
        console.log(typeof check3 === 'undefined');// False

As you can see undeclared variables, or declared but not initialized, both are assigned a type of undefined. Notice declared variables that are not initialized are assigned a value of undefined, the primitive value type but variables that do not exist are undefined types.

null has nothing to do with missing variables or variables not yet assigned values, as null is still a value. So anything with a null is already declared and initialized. Also notice a variable assigned a null value is actually an object type unlike undefined types. For example...

        var check4 = null;
        var check5 = 'hello world';

        console.log('What is null?');

        console.log(check4 === undefined);// False
        console.log(check5 === undefined);// False
        console.log(typeof check4 === 'undefined');// False
        console.log(typeof check5 === 'undefined');// False
        console.log(typeof check4);// return 'object'
        console.log(typeof check5);// return 'string'

As you can see each act differently and yet both are primitive values you can assign any variable. Just understand they represent different states of variables and objects.

Related