7 tips for dealing with undefined in JS

Original: dmitripavlutin.com/7-tips-to-h…

Translator: Front-end Xiaozhi

In order to ensure readability, this article uses free translation rather than literal translation.

If you want to read more high-quality articles, please click on the GitHub blog , and hundreds of high-quality articles are waiting for you every year!

About 8 years ago, when the original author started learning JS, he encountered a strange situation where bothundefinedvalue, there is also a null valuenull. What's the obvious difference between them? They both seem to define null and, moreover, comparenull == undefinedThe calculation result istrue.

Most modern languages ​​like Ruby, Python or Java have a null (nilornull), which seems like a reasonable way.

For JavaScript, the interpreter returns when accessing a variable or object property that has not been initializedundefined. E.g:

  1. let company;
  2. company; // => undefined
  3. let person = { name: 'John Smith' };
  4. person.age; // => undefined
  5. copy code

on the other hand,nullIndicates a missing object reference, JS itself does not set a variable or object property tonull.

Some native methods likeString.prototype.match(), can returnnullto represent missing objects. Take a look at the example below:

  1. let array = null;
  2. array; // => null
  3. let movie = { name: 'Starship Troopers', musicBy: null };
  4. movie.musicBy; // => null
  5. 'abc'.match(/[0-9]/); // => null
  6. copy code

Due to the permissive nature of JS, it is easy for developers to access uninitialized values, and I also made this mistake.

Often, this dangerous operation generatesundefinedrelated errors to quickly end the script. Related common error messages are:

  • TypeError: 'undefined' is not a function
  • TypeError: Cannot read property '<prop-name>' of undefined
  • type errors

JS developers can understand the irony of this joke:

  1. function undefined() {
  2. // problem solved
  3. }
  4. copy code

In order to reduce the risk of such errors, it is necessary to understand the generationundefinedCase. What's more important is to suppress its appearance and prevent its propagation in the application, thereby improving the durability of the code.

Let's discuss in detailundefinedand its impact on code security.

what the hell is undefined

JS has 6 basic types

  • Boolean: trueorfalse
  • Number: 1, 6.7, 0xFF
  • String: "Gorilla and banana"
  • Symbol: Symbol("name") (starting ES2015)
  • Null: null
  • Undefined: undefined.

and a separateObjecttype:{name: "Dmitri"}, ["apple", "orange"].

According to the ECMAScript specification , from 6 primitive types,undefinedis a special value that has its ownUndefinedtype.

The default value when the variable is not assigned a value isundefined.

The standard clearly defines that when accessing uninitialized variables, non-existing object properties, non-existing array elements, etc., you will receive aundefinedvalue of . E.g

  1. let number;
  2. number; // => undefined
  3. let movie = { name: 'Interstellar' };
  4. movie.year; // => undefined
  5. let movies = ['Interstellar', 'Alexander'];
  6. movies[3]; // => undefined
  7. copy code

The general flow of the above code:

  • uninitialized variablenumber
  • a non-existent object propertymovie.year
  • or no array element movies 3

will be defined asundefined.

The ECMAScript specification definesundefinedtype of value

Undefined type is its only valueundefinedType of value.

in this sense,typeof undefinedReturns the "undefined" string

  1. typeof undefined === 'undefined'; // => true
  2. copy code

certainlytypeofA good way to verify that a variable containsundefinedthe value of

  1. let nothing;
  2. typeof nothing === 'undefined'; // => true
  3. copy code

2. Create undefined common scenarios

2.1 Uninitialized variables

A declared variable that has not been assigned a value (uninitialized) defaults toundefined.

  1. let myVariable;
  2. myVariable; // => undefined
  3. copy code

myVariableDeclared, but not yet assigned, the default value isundefined.

An efficient way to solve the problem of uninitialized variables is to assign initial values ​​whenever possible. The less variables are in the uninitialized state, the better. Ideally, you can declareconst myVariable ='Initial value'Specifying a value immediately afterwards is not always possible.

Tip 1: Use let and const instead of var

In my opinion, one of the best features of ES6 is to useconstandletNew way to declare variables.constandletHas block scope (as opposed to the old function scopewasConversely), exists in a temporary dead zone until the declaration line .

When a variable receives a value once and permanently, it is recommended to useconstdeclaration, which creates an immutable binding.

constA nice feature of is that it must be a variableconst myVariable ='initial'Assign an initial value. The variable is not exposed to the uninitialized state, and accessundefinedis not possible.

The following example checks a function that verifies whether a word is a palindrome:

  1. function isPalindrome(word) {
  2. const length = word.length;
  3. const half = Math.floor(length / 2);
  4. for (let index = 0; index < half; index++) {
  5. if (word[index] !== word[length - index - 1]) {
  6. return false;
  7. }
  8. }
  9. return true;
  10. }
  11. isPalindrome('madam'); // => true
  12. isPalindrome ( 'hello' ); // => false
  13. copy code

lengthandhalfThe variable is assigned a value once. declare them asconstSeems reasonable since these variables don't change.

If you need to rebind the variable (i.e. multiple assignments), applyletstatement. Whenever possible, assign an initial value to it immediately, for example,let index = 0.

then usewasAs a statement, relative to ES6, the recommendation is to stop using it entirely.

wasDeclared variable mentions are hoisted to the top of the entire function scope. Can be declared somewhere at the end of the function scopewasvariable, but it can still be accessed before the declaration: the value of the corresponding variable isundefined.

Instead, useletorconstThe variable cannot be accessed before the declared variable. This happens because the variable is in a temporary dead zone until it is declared . This is good, because then there is less opportunity to accessundefinedvalue.

uselet(instead of var) the updated example above throwsReferenceErrorError because variable in temporary dead zone cannot be accessed.

  1. function bigFunction() {
  2. // code...
  3. myVariable; // => Throws 'ReferenceError: myVariable is not defined'
  4. // code...
  5. let myVariable = 'Initial value';
  6. // code...
  7. myVariable; // => 'Initial value'
  8. }
  9. bigFunction();
  10. copy code

Tip 2: Increase Cohesion

Cohesion describes the degree to which the elements of a module (namespaces, classes, methods, code blocks) are cohesive. A measure of cohesion is often referred to as high cohesion or low cohesion.

High cohesion is preferred because it suggests designing the elements of a module to focus only on a single task, which constitutes a module.

  • Focused and understandable: it is easier to understand the function of the module

  • Maintainable and easier to refactor: changes in a module affect fewer modules

  • Reusable: Focus on a single task, making modules easier to reuse

  • Testable: Modules that focus on a single task can be more easily tested

High cohesion and low coupling are characteristics of a well-designed system.

The code block itself may be viewed as a small module, and in order to be as cohesive as possible, variables need to be kept as close as possible to the code block where they are used.

For example, if a variable exists only to form a block scope, do not expose this variable to the outer block scope, as the outer block should not care about this variable.

A classic example of unnecessarily extending the lifetime of a variable is in a functionforUse of loops:

  1. function someFunc(array) {
  2. var index, item, length = array.length;
  3. // some code...
  4. // some code...
  5. for (index = 0; index < length; index++) {
  6. item = array[index];
  7. // some code...
  8. }
  9. return 'some result';
  10. }
  11. copy code

index,itemandlengthVariables are declared at the beginning of the function body, however, they are only used at the end, so what's wrong with this way?

From the declaration at the top toforThe variables index and item in the statement are uninitialized, and the value isundefined. They have unreasonably long lifetimes across the entire function scope.

A better approach is to move these variables as much as possible to where they are used:

  1. function someFunc(array) {
  2. // some code...
  3. // some code...
  4. const length = array.length;
  5. for (let index = 0; index < length; index++) {
  6. const item = array[index];
  7. // some
  8. }
  9. return 'some result';
  10. }
  11. copy code

indexanditemvariables exist only inforwithin the scope of the statement,forIt doesn't make any sense outside of it.lengthThe variable is also declared close to where it is used.

Why is the modified version better than the original version? There are a few main points:

  • variable not exposedundefinedstate, so no accessundefinedrisks of

  • Moving variables as close as possible to where they are used increases the readability of the code

  • Highly cohesive code blocks are easier to refactor and extract into separate functions when necessary

2.2 Accessing properties that do not exist

When accessing a non-existent object property, JS returnsundefined.

Let's illustrate this with an example:

  1. let favoriteMovie = {
  2. title: 'Blade Runner'
  3. };
  4. favoriteMovie.actors; // => undefined
  5. copy code

favoriteMovieis a single attributetitleObject. Using property accessorsfavoriteMovie.actorsaccess non-existent propertyactorswill be calculated asundefined.

Accessing a non-existing property by itself does not throw an error, but problems arise when trying to get data from a non-existing property value. Common mistakes areTypeError: Cannot read property <prop> of undefined.

Slightly modify the previous code snippet to illustrateTypeError throw:

  1. let favoriteMovie = {
  2. title: 'Blade Runner'
  3. };
  4. favoriteMovie.actors[0];
  5. // TypeError: Cannot read property '0' of undefined
  6. copy code

favoriteMovieno attributeactors,sofavoriteMovie.actorsthe value ofundefined. Therefore, using the expressionfavoriteMovie.actors[0]accessundefinedThe first item of the value throwsTypeError.

JS allows access to non-existent properties, and this feature of allowing access can be confusing: properties may or may not be set, and the ideal way to get around this is to restrict an object to always define the properties it holds.

Unfortunately, we often have no control over objects. These objects may have different sets of properties in different scenarios, so all of these scenarios must be handled manually:

Then we implement a functionappend(array, toAppend), its main function adds new elements at the beginning and/or end of an array.toAppendThe parameter accepts an object with properties:

  • first: element is inserted at the beginning of the array

  • last: The element is inserted at the end of the array.

The function returns a new array instance without changing the original array (i.e. it is a pure function).

append()The first version of , which looks simpler, looks like this:

  1. function append(array, toAppend) {
  2. const arrayCopy = array.slice();
  3. if (toAppend.first) {
  4. arrayCopy.unshift(toAppend.first);
  5. }
  6. if (toAppend.last) {
  7. arrayCopy.push(toAppend.last);
  8. }
  9. return arrayCopy;
  10. }
  11. append([2, 3, 4], { first: 1, last: 5 }); // => [1, 2, 3, 4, 5]
  12. append(['Hello'], { last: 'World' }); // => ['Hello', 'World']
  13. append([8, 16], { first: 4 }); // => [4, 8, 16]
  14. copy code

becausetoAppendobject can be omittedfirstorlastproperty, so it must be verifiedtoAppendwhether these properties exist in . If the property does not exist, the property accessor value isundefined.

an examinationfirstorlastIs the propertyundefined, under the condition ofif(toappendix .first){}Wa if(toappendix .last){}Verify in:

This method has a disadvantage,undefined,false,null,0,NaNand''is a false value.

existappend()In the current implementation of , this function does not allow insertion of dummy elements:

  1. append([10], { first: 0, last: false }); // => [10]
  2. copy code

0andfalseis fictitious. becauseif(toAppend.first){}andif(toAppend.last){}actually withfalsiesto compare, so the elements are not inserted into the array, the function returns the initial array[10]without any modification.

The following tips explain how to properly check for the existence of a property.

Tip 3: Check if the property exists

JS provides many ways to determine if an object has a specific property:

  • obj.prop!== undefined: directly withundefinedCompare

  • typeof obj.prop!=='undefined': Validate property value type

  • obj.hasOwnProperty('prop'): Verify that the object has its own properties

  • 'prop' in obj: Verify that the object has its own properties or inherited properties

My suggestion is to useinoperator, its syntax is short and concise.inThe presence of the operator indicates an explicit intent to check whether an object has a particular property without accessing the actual property value.

obj.hasOwnProperty('prop')is also a good solution, it is better thaninThe operator is slightly longer and is only validated in the object's own properties.

involving andundefinedComparing the remaining two ways might work, but in my opinion,obj.prop!== undefinedandtypeof obj.prop!=='undefined'looks verbose and weird and exposes direct handlingundefinedsuspicious path. .

let's useinoperator improvementsappend(array, toAppend)function:

  1. function append(array, toAppend) {
  2. const arrayCopy = array.slice();
  3. if ('first' in toAppend) {
  4. arrayCopy.unshift(toAppend.first);
  5. }
  6. if ('last' in toAppend) {
  7. arrayCopy.push(toAppend.last);
  8. }
  9. return arrayCopy;
  10. }
  11. append([2, 3, 4], { first: 1, last: 5 }); // => [1, 2, 3, 4, 5]
  12. append([10], { first: 0, last: false }); // => [0, 10, false]
  13. copy code

'first' in toAppend(and'last' in toAppend) is when the corresponding attribute existstrue, otherwisefalse.inThe use of the operator solves the insertion of dummy elements0andfalseThe problem. now at[10]Adding these elements at the beginning and end of the[0,10,false].

Tip 4: Destructuring Access to Object Properties

When accessing object properties, it is sometimes necessary to indicate a default value if the property does not exist. can useinand the ternary operator to achieve this.

  1. const object = { };
  2. const prop = 'prop' in object ? object.prop : 'default';
  3. prop; // => 'default'
  4. copy code

The use of the ternary operator syntax becomes daunting when the number of properties to check increases. For each property, a new line of code had to be created to handle the default value, which added an ugly wall full of similar-looking ternary operators.

For a more elegant approach, ES6 object destructuring can be used.

Object destructuring allows extracting object property values ​​directly into variables and setting default values ​​when the property does not exist, avoiding direct processingundefinedconvenient syntax.

In fact, attribute extraction now looks short and meaningful:

  1. const object = { };
  2. const { prop = 'default' } = object;
  3. prop; // => 'default'
  4. copy code

To see this in action, let's define a useful function that wraps a string in quotes.quote(subject, config)Accepts the first argument as a string to wrap. second parameterconfigis an object with the following properties:

  • char: the wrapped character, e.g.'(single quotes) or"(double quotes), default is".

  • skipIfQuoted: boolean to skip the quote if the string is already quoted, defaults totrue.

Using the advantages of object destructor, let's implementquote()

  1. function quote(str, config) {
  2. const { char = '"', skipIfQuoted = true } = config;
  3. const length = str.length;
  4. if (skipIfQuoted
  5. && str[0] === char
  6. && str[length - 1] === char) {
  7. return str;
  8. }
  9. return char + str + char;
  10. }
  11. quote('Hello World', { char: '*' }); // => '*Hello World*'
  12. quote('"Welcome"', { skipIfQuoted: true }); // => '"Welcome"'
  13. copy code

const {char = '", skipifquote = true} = configDestructuring assignment in one line fromconfigobject extractioncharandskipifquoteAttributes. ifconfigThere are some properties in the object that are not available, then destructuring assignment will set default values:charfor'"',skipifquoteforfalse.

There is still room for improvement in this feature. Let's move the destructuring assignment directly to the parameter section. and forconfigThe parameter sets a default value (empty object{}) to skip the second parameter if the default setting is sufficient.

  1. function quote(str, { char = '"', skipIfQuoted = true } = {}) {
  2. const length = str.length;
  3. if (skipIfQuoted
  4. && str[0] === char
  5. && str[length - 1] === char) {
  6. return str;
  7. }
  8. return char + str + char;
  9. }
  10. quote('Hello World', { char: '*' }); // => '*Hello World*'
  11. quote('Sunny day'); // => '"Sunny day"'
  12. copy code

Note that the destructuring assignment replaces the functionconfigparameter. I like this:quote()One line was shortened.={}On the right side of the destructuring assignment, make sure to use the empty object without specifying the second parameter at all.

Object destructuring is a powerful feature that efficiently handles extracting properties from objects. I like the possibility to specify a default value to return when the accessed property doesn't exist. because this avoidsundefinedand the issues associated with dealing with it.

Tip 5: Populate Objects with Default Properties

Objects that are missing some properties can be filled with default values ​​if there is no need to create variables for each property like destructuring assignment.

ES6 Object.assign(target,source1,source2,...)Copies the values ​​of all enumerable own properties from one or more source objects to the target object, and the function returns the target object.

For example, need to accessunsafeOptionsA property of an object that does not always contain its full set of properties.

To avoid fromunsafeOptionsTo access non-existent properties, let's make some adjustments:

  • Defines a property that contains default property valuesdefaultsobject

  • transferObject.assign({},defaults,unsafeOptions)to construct new objectsoptions. new object fromunsafeOptionsReceives all properties, but missing properties fromdefaultsobject acquisition.

  1. const unsafeOptions = {
  2. fontSize: 18
  3. };
  4. const defaults = {
  5. fontSize: 16,
  6. color: 'black'
  7. };
  8. const options = Object.assign({}, defaults, unsafeOptions);
  9. options.fontSize; // => 18
  10. options.color; // => 'black'
  11. copy code

unsafeOptionscontains onlyfontSizeAttributes.defaultsObject Definition PropertiesfontSizeandcolorthe default value of .

Object.assign()Take the first argument as the target object{}. target object fromunsafeOptionssource object receivefontSizeThe value of the property. and peopledefaultsobject acquisitioncolorattribute value, becauseunsafeOptionsdoes not containcolorAttributes.

The order in which source objects are enumerated is important: later source object properties override earlier source object properties.

now securely accessibleoptionsany property of the object, includingoptions.colorin the initialunsafeOptionsis not available in .

There is also an easy way to use the spread operator in ES6:

  1. const unsafeOptions = {
  2. fontSize : 18
  3. };
  4. const defaults = {
  5. fontSize : 16 ,
  6. color: 'black'
  7. };
  8. const options = {
  9. ...defaults,
  10. ...unsafeOptions
  11. };
  12. options.fontSize; // => 18
  13. options.color; // => 'black'
  14. copy code

Object initializers fromdefaultsandunsafeOptionsSource object extended properties. The order in which source objects are specified is important, as later source object properties override earlier source objects.

Populating incomplete objects with default property values ​​is an effective strategy for making your code safe and durable. In either case, the object always contains the full set of properties: and cannot be generatedundefinedproperties.

2.3 Function parameters

Function parameters implicitly default toundefined.

In general, functions defined with a certain number of arguments should be called with the same number of arguments. In this case the parameter gets the expected value

  1. function multiply(a, b) {
  2. a; // => 5
  3. b; // => 3
  4. return a * b;
  5. }
  6. multiply(5, 3); // => 15
  7. copy code

transfermultiply(5,3)make parameteraandbreceive the corresponding5and3value, return result:5 * 3 = 15.

What happens when you omit arguments when calling?

  1. function multiply(a, b) {
  2. a; // => 5
  3. b; // => undefined
  4. return a * b;
  5. }
  6. multiply ( 5 ); // => NaN
  7. copy code

functionmultiply(a, b){}by two parametersaandbdefinition. transfermultiply(5)execute with one argument: the result one argument is5,butbparameter isundefined.

Tip 6: Use default parameter values

Sometimes a function doesn't need the full set of arguments to call, and you can simply set default values ​​for arguments that don't have values.

Looking back at the previous example, let's make an improvement ifbIf the parameter is not defined, it is assigned a default value2:

  1. function multiply(a, b) {
  2. if (b === undefined) {
  3. b = 2;
  4. }
  5. a; // => 5
  6. b; // => 2
  7. return a * b;
  8. }
  9. multiply(5); // => 10
  10. copy code

While the provided method of assigning a default value works, it is not recommended to directly interact withundefinedvalue to compare. It's verbose and looks like a hack.

ES6 defaults can be used here:

  1. function multiply(a, b = 2) {
  2. a; // => 5
  3. b; // => 2
  4. return a * b;
  5. }
  6. multiply(5); // => 10
  7. multiply(5, undefined); // => 10
  8. copy code

2.4 Function return value

implicitly, noreturnstatement, JS function returnsundefined.

In JS, there is noreturnThe function of the statement returns implicitlyundefined:

  1. function square(x) {
  2. const res = x * x;
  3. }
  4. square(2); // => undefined
  5. copy code

square()The function does not return the calculation result, the result when the function is calledundefined.

whenreturnReturns by default when there is no expression after the statementundefined.

  1. function square(x) {
  2. const res = x * x;
  3. return;
  4. }
  5. square(2); // => undefined
  6. copy code

return;The statement is executed, but it does not return any expression, and the result of the call is alsoundefined.

  1. function square(x) {
  2. const res = x * x;
  3. return res;
  4. }
  5. square(2); // => 4
  6. copy code

Tip 7: Don't Trust Automatic Semicolon Insertion

The following statement list in JS must be preceded by a semicolon(;)end:

  • empty statement

  • let, const, var, import, exportstatement

  • expressive statement

  • debuggerstatement

  • continuestatement,breakstatement

  • throwstatement

  • returnstatement

If you use one of the above declarations, try to be sure to specify a semicolon at the end :

  1. function getNum () {
  2. let num = 1 ;
  3. return num;
  4. }
  5. getNum(); // => 1
  6. copy code

letstatement andreturnAt the end of the statement, a semicolon is mandatory .

What happens when you don't want to write these semicolons? For example, we want to reduce the size of the source file.

In this case, ECMAScript provides the Automatic Semicolon Insertion (ASI) mechanism to insert the missing semicolon for you .

With the help of ASI, the semicolons can be removed from the previous example :
function getNum() { // Notice that semicolons are missing let num = 1 return num } getNum() // => 1

The above code is valid JS code, the missing semicolons are automatically inserted for us by ASI.

At first glance, it looks nice. The ASI mechanism allows you to write less unnecessary semicolons, which can make JS code smaller and easier to read.

ASI creates a small but annoying trap. when the newline is atreturnandreturn \n expressionASI automatically inserts a semicolon before the newline (return; \n expression).

inside the functionreturn;? i.e. the function returnsundefined. If you do not understand the mechanism of ASI in detail, unexpectedly returnedundefinedUnexpected problems can arise.

ComegetPrimeNumbers()The value returned by the call:

  1. function getPrimeNumbers() {
  2. return
  3. [ 2 , 3 , 5 , 7 , 11 , 13 , 17 ]
  4. }
  5. getPrimeNumbers() // => undefined
  6. copy code

existreturnThere is a newline between the statement and the array, JS inreturnAfter the semicolon is automatically inserted, the explanation code is as follows:

  1. function getPrimeNumbers() {
  2. return;
  3. [ 2 , 3 , 5 , 7 , 11 , 13 , 17 ];
  4. }
  5. getPrimeNumbers(); // => undefined
  6. copy code

return;make functiongetPrimeNumbers()returnundefinedinstead of the expected array.

This question is removed byreturnand newlines between array literals to resolve:

  1. function getPrimeNumbers() {
  2. return [
  3. 2 , 3 , 5 , 7 , 11 , 13 , 17
  4. ];
  5. }
  6. getPrimeNumbers(); // => [2, 3, 5, 7, 11, 13, 17]
  7. copy code

My suggestion is to study the exact way of automatic semicolon insertion to avoid this.

Of course, neverreturnA newline is placed between the expression and the returned expression.

2.5 The void operator

void <expression>Calculated expression returns regardless of the result of the calculationundefined.

  1. void 1; // => undefined
  2. void (false); // => undefined
  3. void {name: 'John Smith'}; // => undefined
  4. void Math.min(1, 3); // => undefined
  5. copy code

voidOne use case for operators is to limit expression evaluation toundefined, which relies on some side-effects of the evaluation.

3. Undefined array

When accessing an array element with an out-of-bounds index, you getundefined.

  1. const colors = ['blue', 'white', 'red'];
  2. colors[5]; // => undefined
  3. colors[-1]; // => undefined
  4. copy code

colorsThe array has 3 elements, so a valid index is0,1and2.

because of the index5and-1There are no array elements, so accesscolors[5]andcolors[-1]value isundefined.

In JS, you may encounter so-called sparse arrays. These arrays are gapped, that is, at certain indices, no element is defined.

When accessing a gap (aka empty slot) in a sparse array, you also get aundefined.

The following example generates sparse arrays and tries to access their empty slots

  1. const sparse1 = new Array(3);
  2. sparse1; // => [<empty slot>, <empty slot>, <empty slot>]
  3. sparse1[0]; // => undefined
  4. sparse1[1]; // => undefined
  5. const sparse2 = ['white', ,'blue']
  6. sparse2; // => ['white', <empty slot>, 'blue']
  7. sparse2[1]; // => undefined
  8. copy code

When using arrays, in order to avoid gettingundefined, make sure to use valid array indices and avoid creating sparse arrays.

4. Difference between undefined and null

A legitimate question arises:undefinedandnullWhat is the main difference between? Both of these special values ​​represent the empty state.

The main difference is thatundefinedrepresents the value of a variable that has not yet been initialized,nullIndicates that the object does not exist on purpose.

Let's explore the difference between them with some examples.

number is defined but not assigned a value.

  1. let number;
  2. number; // => undefined
  3. copy code

numberThe variable is undefined, which clearly indicates an uninitialized variable.

The same concept of uninitialized also occurs when accessing a non-existing object property

  1. const obj = { firstName: 'Dmitri' };
  2. obj.lastName; // => undefined
  3. copy code

becauseobjdoes not exist inlastNameproperty, so JS correctly willobj.lastNameCalculated asundefined.

In other cases, you know that the variable expects to hold an object or a function that returns an object. But for some reason you can't instantiate that object. in this case,nullis a meaningful indicator of missing objects.

E.g,clone()is a function that clones a normal JS object, the function will return an object

  1. function clone(obj) {
  2. if (typeof obj === 'object' && obj !== null) {
  3. return Object.assign({}, obj);
  4. }
  5. return null;
  6. }
  7. clone({name: 'John'}); // => {name: 'John'}
  8. clone(15); // => null
  9. clone(null); // => null
  10. copy code

However, it is possible to call with non-object argumentsclone():15ornull(or usually a primitive value,nullorundefined). In this case, the function cannot create a clone, so it returnsnull- an indicator of a missing object.

typeofoperator distinguishes between the two values

  1. typeof undefined; // => 'undefined'
  2. typeof null; // => 'object'
  3. copy code

strict equality operator===can correctly distinguishundefinedandnull:

  1. let nothing = undefined;
  2. let missingObject = null;
  3. nothing === missingObject; // => false
  4. copy code

Summarize

undefinedThe existence of JS is a result of the permissive nature of JS, which allows the use of:

  • uninitialized variable
  • Object property or method that does not exist
  • access array element at out-of-bounds index
  • the result of a call to a function that returns no result

In most cases directly withundefinedMaking comparisons is a bad practice. An effective strategy is to reduce the amount ofundefinedOccurrence of keywords:

  • Reduce the use of uninitialized variables

  • Make the variable lifetime short and close to where it's used

  • Assign initial values ​​to variables whenever possible

  • More perfunctory const and let

  • Use default values ​​for insignificant function arguments

  • Validate property exists or populate unsafe object with default property

  • Avoid sparse arrays

The bugs that may exist after the code is deployed cannot be known in real time. In order to solve these bugs afterwards, a lot of time is spent on log debugging. By the way, I recommend a useful bug monitoring tool , Fundebug .

comminicate

The dry goods series articles are summarized as follows, if you feel good, click Star, and welcome to join the group to learn from each other.

github.com/qq449245884…

I am Xiao Zhi, author of the public account "Moving the World", and I am a lover of front-end technology. I will often share the dry goods I have learned and watched , and encourage each other on the way to advanced!

Follow the official account, reply to the benefits in the background , you can see the benefits, you know.

Reprinted at: https://juejin.im/post/5d422cf26fb9a06b1b199f32

Related: 7 tips for dealing with undefined in JS