- Undefined vs. null revisited
- Original author: Dr. Axel Rauschmayer
- The Nuggets translation Project
- Permanent link to this article: github.com/xitu/gold-m…
- Translator: Hoarfroster
- Proofread by Moonball and felixliao
Revisit undefined and NULL
Many programming languages have a type that represents a null value, called NULL. It indicates that a variable does not currently point to any object — for example, when a variable has not been initialized.
JavaScript, on the other hand, has two types that represent null values: undefined and null. In this article, we’ll test the differences and how to pick the best types or avoid using them.
undefined
vs. null
The two values are very similar and are often used interchangeably, so the differences between them are subtle.
undefined
,null
Comparison on the ECMAScript language standard
The ECMAScript language standard describes them as follows:
undefined
Used when a variable has not yet been assigned a value.provenancenull
Represents any intentional default object value.provenance
We’ll explore how we, as programmers, can best use these two values in a moment.
Two null values — an irreparable error
Having two values representing null values in JavaScript at the same time is now considered a design error (even by Brendan Eich, the father of JavaScript).
So why not remove one of these two values from JavaScript? One of the core tenets of JavaScript is to never break backward compatibility. This principle has advantages, but it also has the biggest disadvantage of not being able to remedy design errors.
undefined
和 null
The history of the
In Java (a language that influences many aspects of JavaScript) the initial value depends on the static type of a variable:
- A variable of type object value is initialized to
null
. - Each base type has its initial value, for example
int
An integer corresponding to the0
.
In JavaScript, each variable can store either the object value or the original value, meaning that if null means that it is not an object, JavaScript also needs an initial value to indicate that it is neither an object nor has the original value, which is called undefined.
undefined
The appearance of
If a variable myVar has not been initialized, its value is undefined:
let myVar;
assert.equal(myVar, undefined);
Copy the code
If a.unknownprop attribute does not exist, accessing it generates undefined:
const obj = {};
assert.equal(obj.unknownProp, undefined);
Copy the code
If a function does not explicitly return anything, undefined is returned by default:
function myFunc() {
}
assert.equal(myFunc(), undefined);
Copy the code
If a function has a return statement but does not specify any return value, undefined is also returned by default:
function myFunc() {
return;
}
assert.equal(myFunc(), undefined);
Copy the code
If a parameter x does not pass an argument, it is initialized to undefined:
function myFunc(x) {
assert.equal(x, undefined);
}
myFunc();
Copy the code
Through the obj? .someprop access optional chain returns undefined when obj is undefined or null:
> undefined? .someProp undefined > null? .someProp undefinedCopy the code
null
The appearance of
The prototype of an object is either another object or null at the end of the prototype chain. Prototype:
> Object.getPrototypeOf(Object.prototype)
null
Copy the code
If we use a regular expression (such as /a/) to match a string (such as x), we get either an object holding the match data (if the match is successful) or NULL (if the match fails).
> /a/.exec('x')
null
Copy the code
The JSON data format does not support undefined, but only null:
> JSON.stringify({a: undefined, b: null})
'{"b":null}'
Copy the code
Designed to deal withundefined
和 null
The operator of
undefined
And default parameter values
The default value of a parameter is used when:
- This parameter is ignored.
- This parameter is assigned to
undefined
Value.
Here’s an example:
function myFunc(arg = 'abc') {
return arg;
}
assert.equal(myFunc('hello'), 'hello');
assert.equal(myFunc(), 'abc');
assert.equal(myFunc(undefined), 'abc');
Copy the code
Undefined also fires the default parameter value when the value pointing to it is a meta value.
The following example demonstrates where this feature can be 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 default value for the STR argument, and when this parameter is ignored, we forward the state to concat() to select the default value.
undefined
Destruct the default values
Defaults under deconstruction work like parameter defaults — if variables do not match in the data or match undefined, they are used:
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 chain
If you use value? .prop uses optional chains:
- if
value
是undefined
或null
Will returnundefined
. In other words, ifvalue.prop
If an error is thrown, it will returnundefined
. - Otherwise it will 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 two operations work similarly:
obj? Issue. [expr]// Optional dynamic property accessfunc? . (the « arg0 », « arg1 »)// Optional function or method call
Copy the code
undefined
,null
And empty
The null merge operator?? Allows us to use the default value when a value is undefined or 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
The null merge assignment operator?? = merges the null merge operator with the assignment operator:
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
To deal withundefined
与 null
The following sections explain the most common ways to handle undefined and NULL in our code:
The actual value is neitherundefined
Is notnull
For example, we might want the attribute file.title to always exist and always be a string, so there are two common ways to do this.
Note that in this blog post, we only check for undefined and NULL, not for strings. You need to decide for yourself whether you want to add a checker as an additional security measure.
At the same time is prohibitedundefined
和 null
Such as:
function createFile(title) {
if (title === undefined || title === null) {
throw new Error('`title` must not be nullish');
}
/ /...
}
Copy the code
Why did you choose this method?
-
We want to handle undefined and null the same way, because JavaScript code often does this, for example:
// Check if an attribute exists if(! obj.requiredProp) { obj.requiredProp =123; } // Use the default values through the null merge operator const myValue = myParameter ?? 'some default'; Copy the code
-
If something goes wrong in our code and we let undefined or NULL appear, we need to let it end execution as soon as possible and throw an error.
At the same timeundefined
和 null
Use default values
Such as:
function createFile(title) { title ?? ='(Untitled)';
/ /...
}
Copy the code
We cannot use the parameter default because it will only be triggered by undefined. Here we rely on the null merge assignment operator?? =.
Why did you choose this method?
- We want to be treated the same way
undefined
和null
(See above). - We want our code to be treated quietly but forcefully
undefined
和null
.
undefined
或 null
Is an ignored value
For example, we might want the attribute file.title to be a string or an ignored value (that is, file has no title), and there are several ways to do this.
null
Is the ignored value
Such as:
function createFile(title) {
if (title === undefined) {
throw new Error(' 'title' should not be undefined');
}
return {title};
}
Copy the code
Alternatively, undefined can trigger the default:
function createFile(title = '(Untitled)') {
return {title};
}
Copy the code
Why choose this method?
- We need a null value to indicate that we are ignored.
- We don’t want null values to trigger parameter defaults and break them.
- We want to convert the null-valued string to JSON (which we can’t
undefined
Processing).
undefined
Is the ignored value
Such as:
function createFile(title) {
if (title === null) {
throw new Error(' 'title' should not be null');
}
return {title};
}
Copy the code
Why choose this method?
- We need a null value to indicate that we are ignored.
- We do want null values to trigger arguments or destruct defaults.
One drawback of undefined is that it is often given by accident in JavaScript — in uninitialized variables, typos in attribute names, forgetting to return content from functions, etc.
Why not at the same timeundefined
和 null
As a ignored value, right?
It makes sense to treat both undefined and NULL as “null” when a value is received. However, when we create values, we don’t want ambiguity to avoid unnecessary trouble.
This points to another Angle: what if we need a ignored value, but don’t want to use undefined or NULL as the ignored value? Check it out below:
Other ways to handle ignored values
Special values
We can create a special value that will be used whenever.title is ignored:
const UNTITLED = Symbol('UNTITLED');
const file = {
title: UNTITLED,
};
Copy the code
Null object mode
The Null object pattern comes from OOP (object-oriented programming) :
- All subclasses of a common superclass have the same interface.
- Each subclass implements a different pattern for example use.
- One of these patterns is
null
.
In the following, UntitledFile inherits 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 can also use the empty object mode only for the title, rather than the entire file object.
“Maybe” type
The “maybe” type is a functional programming technique:
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 could have encoded “just” and “nothing” with arrays, but the nice thing about our approach is that TypeScript supports them well (via recognizable unions).
My method
I don’t like using undefined as a ignored value for three reasons:
undefined
It’s usually an accident in JavaScript.undefined
Triggers default values for parameters and destructions (some people prefer it for some reasonundefined
).
Therefore, if special values are required, one of two methods can be used:
- I will be
null
Used as a ignored value. (TypeScript supports this approach relatively well, by the way.) - I avoided simultaneity by using one of the techniques above
undefined
和null
The advantage is that the code is cleaner, and the disadvantage is that it requires more work.
- This article is participating in the “Nuggets 2021 Spring Recruitment Campaign”, click to see the details of the campaign
If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.
The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.