🥳 welcome interested partners, do something meaningful together! Translator: chressYu

I launched a weekly translation project at github.com and fedarling.github. IO

Currently, there is still a lack of like-minded partners, which is purely personal interest. Of course, it will also help to improve English and front-end skills. Requirements: English should not be too bad, proficient in using Github, persistent, modest, and responsible for what they do.

If you want to participate, you can either click here to view it or send an issue message to the warehouse. My blog also has specific personal contact information: daodaolee.cn

Most programming languages have a definition of “null”, which is NULL. It indicates that the variable does not currently point to an object — for example, when it has not been initialized. In contrast to other languages, JavaScript contains two such null values: undefined and null. In this article, we’ll explore the differences between them and how to best use or avoid them.

undefined vs. null

The two values are very similar and can be used interchangeably. So the difference is subtle.

Undefined and null in ECMAScript

ECMAScript defines undefined and null as follows:

  • Undefinde: When a variable is not assigned a value.
  • Null: does not represent any object;

We’ll see how best to deal with these two values as a programmer later.

Two null values — an error that cannot be deleted

Having two null values in Javascript is still considered a bad design (even by Javascript’s creator, Brendan Eich). So why not delete one of them? One of the core tenets of JavaScript is never to break down compatibility. This principle has many advantages. But its biggest drawback is that it can’t get rid of bad design.

History of undefined and null

In Java (with much inspiration for JavaScript), the initialization value depends on the static type of the variable: null objects are included when initialized. Any base type contains the value it initializes. For example, int is initialized to 0. In JavaScript, each variable can contain both an object value and a default value. Therefore, if null means “not an object”, JavaScript requires an initial value that means “neither an object nor an initialized value”. The base value is undefined.

How is undefined created

If a variable myVar does not initialize a value, its value is undefined:

let myVar;
assert.equal(myVar, undefined);
Copy the code

If there is no unknownProp in an object, undefined is generated when accessing the unknownProp property:

const obj = {};
assert.equal(obj.unknownProp, undefined);
Copy the code

If a function does not have an explicit return value, the function returns undefined:

function myFunc() {}
assert.equal(myFunc(), undefined);
Copy the code

If a function return does not return any value, it returns undefined:

function myFunc() {
  return;
}
assert.equal(myFunc(), undefined);
Copy the code

If a function’s parameter x is omitted when called, it will be undefined:

function myFunc(x) {
  assert.equal(x, undefined);
}
myFunc();
Copy the code

If a variable is undefined or null, ojb? SomeProp returns undefined:

undefined? .someProp// undefined
null? .someProp// undefined
Copy the code

How is NULL generated

The stereotype of an object is either an object or null at the end of the stereotype chain. Prototype:

Object.getPrototypeOf(Object.prototype)
// null
Copy the code

If we match the string ‘x’ with the regular expression /a/, we return null if the match fails:

/a/.exec('x')
// null
Copy the code

JSON is null-only and undefined is not supported when calling json. stringify

JSON.stringify({a: undefined.b: null})
// '{"b":null}'
Copy the code

How to handle undefined and NULL

Parameter default values are used for:

  1. When parameters are missing
  2. When the input parameter is not defined

Such as:

function myFunc(arg='abc') {
  return arg;
}
assert.equal(myFunc('hello'), 'hello');
assert.equal(myFunc(), 'abc');
assert.equal(myFunc(undefined), 'abc');
Copy the code

Undefined can also trigger parameters to use default values.

The following example illustrates where this is useful:

function concat(str1=' ', str2=' ') {
  return str1 + str2;
}
function twice(str) { // (A)
  return concat(str, str);
}
Copy the code

In line A, we do not specify A parameter default value for STR. When this parameter is missing, the twice function uses the default value.

Undefined and deconstruct default values

If the variable does not match or matches undefined, it will use the default value:

const [a='a'] = [];
assert.equal(a, 'a');

const [b='b'] = [undefined];
assert.equal(b, 'b');

const {prop: c='c'} = {};
assert.equal(c, 'c');

const {prop: d='d'} = {prop: undefined};
assert.equal(d, 'd');
Copy the code

Undefined, NULL, and optional chains

When using the optional chain value? . When the prop:

  • If value is undefined or null, undefined is returned, that is, when value.prop throws an exception.
  • Otherwise, return value.prop.
function getProp(value) {
  // optional static property access
  returnvalue? .prop; } assert.equal(getProp({prop: 123}), 123);
assert.equal(getProp(undefined), undefined);
assert.equal(getProp(null), undefined);
Copy the code

The following operations are similar:

obj? Issue. [expr]// optional dynamic property accessfunc? . (the « arg0 », « arg1 »)// optional function or method call
Copy the code

Undefined, NULL, and null value combinations

Null-value merge operator?? Let’s use the default values when we encounter undefined and NULL:

undefined ?? 'default value'
// 'default value'
null ?? 'default value'
// 'default value'

0 ?? 'default value'
/ / 0
123 ?? 'default value'
/ / 123
' ' ?? 'default value'
/ /"
'abc' ?? 'default value'
// 'abc'
Copy the code

Logical null assignment?? = merges null value merges and assignment:

function setName(obj) {
  obj.name ??= '(Unnamed)';
  return obj;
}
assert.deepEqual(
  setName({}),
  {name: '(Unnamed)'}); assert.deepEqual( setName({name: undefined{}),name: '(Unnamed)'}); assert.deepEqual( setName({name: null{}),name: '(Unnamed)'}); assert.deepEqual( setName({name: 'Jane'{}),name: 'Jane'});Copy the code

How to handle null and undefined

The following sections explain the most common ways to handle undefined and NULL in our own code.

Undefined and null are not used as actual values

For example, we might want the property file.title to always exist and always be a string. There are two common ways to do this.

Note that in this blog post, we only consider undefined and NULL, not whether the value is a string. You must decide for yourself whether to implement this as an additional security measure.

Undefined and NULL are disallowed

Such as:

function createFile(title) {
  if (title === undefined || title === null) {
    throw new Error('`title` must not be nullish');
  }
  / /...
}
Copy the code

Why choose this method?

  • We want colleagues to handle undefined and null, so we often write things like:

    // Detecting if a property exists
    if(! obj.requiredProp) { obj.requiredProp =123;
    }
    
    // Default values via nullish coalescing operator
    const myValue = myParameter ?? 'some default';
    Copy the code
  • If our code is causing problems with undefined and NULL, we want it thrown as soon as possible.

Default values are triggered when undefined and NULL are encountered.

Such as:

function createFile(title) { title ?? ='(Untitled)';
  / /...
}
Copy the code

We cannot use the default value of the function here because it can only be triggered by undefined, so we use the null value merge operator here. =

Why choose this method?

  • We want undefined and NULL to be handled identically.
  • We want our code to handle undefined and NULL exactly

Undefined or null denotes a null value

For example, we might want the attribute file.title to be a string or null (File has no title attribute). There are several ways to do this.

Here we use null to indicate a null value

Such as:

function createFile(title) {
  if (title === undefined) {
    throw new Error('`title` must not be undefined');
  }
  return {title};
}
Copy the code

Or undefined triggers a default value:

function createFile(title = '(Untitled)') {
  return {title};
}
Copy the code

Why choose this approach:

  • We need a null value to indicate none
  • We don’t want null to trigger parameter defaults and destruct defaults.
  • We want the null-valued string to be JSON (so we can’t use undefined).

Undefined denotes a null value

Such as:

function createFile(title) {
  if (title === null) {
    throw new Error('`title` must not be null');
  }
  return {title};
}
Copy the code

Why choose this approach:

  • We need a null value to indicate none
  • We do want null values to trigger parameter defaults and destruct defaults.

One drawback of undefined is that it is often created inadvertently by JavaScript: by an uninitialized variable, a typo in the property name, forgetting to return something from a function, and so on.

Why not use undefined and null at the same time

It makes sense to treat both undefined and NULL as “null” when a value is received. Because, when we create a value, we want it to be unambiguous so that we can easily manipulate the value.

What if we don’t want to use undefined or null to represent a null value? Please look down!

The other way represents a null value

A special value

We can create a special value to indicate that.title is empty:

const UNTITLED = Symbol('UNTITLED');
const file = {
  title: UNTITLED,
};
Copy the code

Empty object mode

The empty object pattern comes from object-oriented programming:

  • Common classes all inherit from the same interface.
  • Each subclass implements a different schema in which an instance operates.
  • One of these patterns is “empty.”

In the example below, UntitledFile implements the “NULL” mode.

// Abstract superclass
class File {
  constructor(content) {
    if (new.target === File) {
      throw new Error('Can' t instantiate this class ');
    }
    this.content = content; }}class TitledFile extends File {
  constructor(content, title) {
    super(content);
    this.title = title;
  }
  getTitle() {
    return this.title; }}class UntitledFile extends File {
  constructor(content) {
    super(content);
  }
  getTitle() {
    return '(Untitled)'; }}const files = [
  new TitledFile('Dear diary! '.'My Diary'),
  new UntitledFile('Reminder: pick a title! ')]; assert.deepEqual( files.map(f= > f.getTitle()),
  [
    'My Diary'.'(Untitled)',]);Copy the code

We could also use just the empty object mode File to handle the title attribute (rather than the entire File object).

Possible types

Write down all possible types to handle the problem:

function getTitle(file) {
  switch (file.title.kind) {
    case 'just':
      return file.title.value;
    case 'nothing':
      return '(Untitled)';
    default:
      throw new Error();
  }
}

const files = [
  {
    title: {kind: 'just'.value: 'My Diary'},
    content: 'Dear diary! '}, {title: {kind: 'nothing'},
    content: 'Reminder: pick a title! ',},]; assert.deepEqual( files.map(f= > getTitle(f)),
  [
    'My Diary'.'(Untitled)',]);Copy the code

We can encode “just” and “nothing” in arrays. The nice thing about our approach is that it is well supported by TypeSript.

A link to the

undefined vs. null revisited

Translation plan