🥳 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:
- When parameters are missing
- 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