This is the 14th day of my participation in the August More Text Challenge

Error handling mechanism

Error instance object

When JavaScript parses or runs, the engine throws an error object whenever an error occurs. JavaScript natively provides the Error constructor, and all thrown errors are instances of that constructor.

var err = new Error('Wrong');
err.message // "error"
Copy the code

In the code above, we call the Error() constructor to generate an instance object, Err. The Error() constructor takes an argument representing an Error message, which can be read from the instance’s message property. After an Error instance object is thrown, the entire program interrupts at the point where the Error occurred and does not proceed further.

The JavaScript language standard only mentions that the Error instance object must have a message attribute, which indicates the message when an Error occurs. Most JavaScript engines also provide name and stack attributes for Error instances, indicating the name of the Error and the stack of the Error, respectively, but these are non-standard and not available in every implementation.

  • Message: error message
  • Name: Error name (non-standard attribute)
  • Stack: Wrong stack (non-standard property)

Using the name and message attributes, you can get a general idea of what went wrong.

if (error.name) {
  console.log(error.name + ':' + error.message);
}
Copy the code

The stack property is used to view the stack when an error occurs.

function throwit() {
  throw new Error(' ');
}

function catchit() {
  try {
    throwit();
  } catch(e) {
    console.log(e.stack); // print stack trace
  }
}

catchit()
// Error
// at throwit (~/examples/throwcatch.js:9:11)
// at catchit (~/examples/throwcatch.js:3:9)
// at repl:1:5
Copy the code

In the above code, the innermost layer of the error stack is the throwit function, then the catchit function, and finally the environment in which the function is run.

Native error type

The Error instance object is the most common Error type, and JavaScript defines six other Error objects on top of it. That is, there are six derived objects of Error.

SyntaxError object

SyntaxError objects are syntax errors that occur when parsing code.

// The variable name is incorrect
var 1a;
// Uncaught SyntaxError: Invalid or unexpected token

// The parentheses are missing
console.log 'hello');
// Uncaught SyntaxError: Unexpected string
Copy the code

All errors in the above code are found during the parsing phase, so SyntaxError is raised. The first error message is “token is invalid” and the second error message is “string does not meet requirements”.

ReferenceError object

The ReferenceError object is an error that occurs when referring to a variable that does not exist.

// Use a variable that does not exist
unknownVariable
// Uncaught ReferenceError: unknownVariable is not defined
Copy the code

Another triggering scenario is to assign a value to an object that cannot be assigned, such as to the result of a function run.

// The left side of the equals sign is not a variable
console.log() = 1
// Uncaught ReferenceError: Invalid left-hand side in assignment
Copy the code

The above code assigns a value to the console.log result, which raises a ReferenceError.

RangeError object

The RangeError object is an error that occurs when the value is outside the valid range. The Number object is out of range, and the function stack exceeds the maximum value.

// The array length must not be negative
new Array(-1)
// Uncaught RangeError: Invalid array length
Copy the code

TypeError object

A TypeError object is an error that occurs when a variable or parameter is not of the expected type. For example, using the new command on primitives such as strings, booleans, values, etc., throws this error because the argument to the new command should be a constructor.

new 123
// Uncaught TypeError: 123 is not a constructor

var obj = {};
obj.unknownMethod()
// Uncaught TypeError: obj.unknownMethod is not a function
Copy the code

The second case of the above code, calling a method where the object does not exist, also raises TypeError because obJ. unknownMethod is undefined, not a function.

URIError object

The URIError object is an error thrown when arguments to urI-related functions are incorrect, It mainly involves encodeURI(), decodeURI(), encodeURIComponent(), decodeURIComponent(), escape() and unescape().

decodeURI('% 2')
// URIError: URI malformed
Copy the code

EvalError object

An EvalError is thrown when the eval function is not executed correctly. This error type is no longer used, but is retained for compatibility with previous code.

conclusion

These six derived errors, along with the original Error object, are constructors. Developers can use them to manually generate instances of error objects. Each of these constructors takes an argument that represents an error message.

var err1 = new Error('Wrong! ');
var err2 = new RangeError('Error, variable out of valid range! ');
var err3 = new TypeError('Error, variable type invalid! ');

err1.message // "Error!"
err2.message // "Error, variable out of valid range!"
err3.message // "Error, invalid variable type!"
Copy the code

Custom error

In addition to the seven error objects that JavaScript natively provides, you can define your own.

function UserError(message) {
  this.message = message || 'Default information';
  this.name = 'UserError';
}

UserError.prototype = new Error(a); UserError.prototype.constructor = UserError;Copy the code

The code above defines a custom Error object, UserError, to inherit from the Error object. This custom type of error can then be generated.

new UserError('This is a custom error! ');
Copy the code

Throw statement

The purpose of a throw statement is to manually interrupt program execution and throw an error.

var x = -1;

if (x <= 0) {
  throw new Error('x must be positive. ');
}
// Uncaught Error: x must be a positive number
Copy the code

In the above code, if the variable x is less than or equal to 0, it manually throws an error telling the user that x is not the correct value, and the entire program is interrupted at this point. As you can see, the Error thrown by the throw is its argument, and here is an instance of an Error object.

Throws can also throw custom errors.

function UserError(message) {
  this.message = message || 'Default information';
  this.name = 'UserError';
}

throw new UserError('Wrong! ');
// Uncaught UserError {message: "Error!" , name: "UserError"}
Copy the code

In the above code, the throw throws a UserError instance.

In fact, a throw can throw any type of value. That is, it can take any value.

// Throws a string
throw 'the Error! ';
/ / Uncaught Error!

// Throws a value
throw 42;
// Uncaught 42

// Throws a Boolean value
throw true;
// Uncaught true

// Throws an object
throw {
  toString: function () {
    return 'Error! '; }};/ / Uncaught {toString: ƒ}
Copy the code

In the case of a JavaScript engine, a throw statement is encountered and the program is aborted. The engine receives information thrown by the throw, which may be an instance of an error or some other type of value.

try… Catch the structure

As soon as an error occurs, the program is aborted. JavaScript provides try… A catch structure that allows error handling, with the option to proceed.

try {
  throw new Error('Wrong! ');
} catch (e) {
  console.log(e.name + ":" + e.message);
  console.log(e.stack);
}
// Error: Error!
// at 
      
       :3:9
      
/ /...
Copy the code

In the above code, the try block throws an error (this example uses a throw statement), and the JavaScript engine immediately transfers execution to the catch block, or the error is caught by the catch block. Catch takes a parameter that represents the value thrown by the try block.

If you are not sure whether certain code will report an error, you can put them in a try… In the catch block for further error handling.

try {
  f();
} catch(e) {
  // Processing error
}
Copy the code

In the above code, if f fails, a catch block is executed and the error is then handled.

After the catch block catches an error, the program does not break and continues to execute as normal.

try {
  throw "Something went wrong.";
} catch (e) {
  console.log(111);
}
console.log(222);
/ / 111
/ / 222
Copy the code

In the above code, an error thrown by the try block is caught by the catch block, and the program continues execution.

Inside a catch block, you can throw an error, or even use a nested try… Structure of the catch.

var n = 100;

try {
  throw n;
} catch (e) {
  if (e <= 50) {
    // ...
  } else {
    throwe; }}// Uncaught 100
Copy the code

In the above code, another error is thrown in the catch code.

To catch different types of errors, a catch block can include a judgment statement.

try {
  foo.bar();
} catch (e) {
  if (e instanceof EvalError) {
    console.log(e.name + ":" + e.message);
  } else if (e instanceof RangeError) {
    console.log(e.name + ":" + e.message);
  }
  // ...
}
Copy the code

In the above code, after catching an error, the catch determines the error type (EvalError or RangeError) and performs different processing.

The finally block of code

try… The catch structure allows a finally block to be added at the end to represent the statement that must be run at the end regardless of an error.

function cleansUp() {
  try {
    throw new Error('Wrong... ');
    console.log('This line will not be executed');
  } finally {
    console.log('Finish the cleanup');
  }
}

cleansUp()
// Complete the cleanup
// Uncaught Error: Error......
// at cleansUp (
      
       :3:11)
      
// at 
      
       :10:1
      
Copy the code

In the above code, there is no catch block, so if an error occurs, the code interrupts execution. The finally code block is executed before execution is interrupted, and then the user is prompted with an error message.

function idle(x) {
  try {
    console.log(x);
    return 'result';
  } finally {
    console.log('FINALLY');
  }
}

idle('hello')
// hello
// FINALLY
Copy the code

In the above code, the try block does not fail and the return statement is included, but the finally block still executes. Also, the return value of this function is result.

The following example shows that the return statement executes before the finally code and only returns after the finally code has finished executing.

var count = 0;
function countUp() {
  try {
    return count;
  } finally {
    count++;
  }
}

countUp()
/ / 0
count
/ / 1
Copy the code

The value of count in the return statement is retrieved before the finally block runs.

Here is a typical scenario for finally block usage.

openFile();

try {
  writeFile(Data);
} catch(e) {
  handleError(e);
} finally {
  closeFile();
}
Copy the code

The above code first opens a file, then writes the file to the try block, and if no errors occur, runs the finally block to close the file; When an error occurs, the catch block is used to handle the error and then the finally block is used to close the file.

Try… catch… Finally The order in which the three are executed.

function f() {
  try {
    console.log(0);
    throw 'bug';
  } catch(e) {
    console.log(1);
    return true; // This sentence would have been deferred until the end of the finally block
    console.log(2); // Will not run
  } finally {
    console.log(3);
    return false; // This sentence overwrites the previous sentence return
    console.log(4); // Will not run
  }

  console.log(5); // Will not run
}

var result = f();
/ / 0
/ / 1
/ / 3

result
// false
Copy the code

In the above code, the finally block is executed before the catch block finishes execution.

In a catch block, a flag that triggers a transition to a finally block includes not only a return statement but also a throw statement.

function f() {
  try {
    throw 'Wrong! ';
  } catch(e) {
    console.log('Caught an internal error');
    throw e; // This sentence would have waited until finally finished
  } finally {
    return false; // Return directly}}try {
  f();
} catch(e) {
  // Not executed here
  console.log('caught outer "bogus"');
}

// An internal error was caught
Copy the code

In the above code, after entering the catch block, the first time a throw statement is encountered, the finally block is executed. The finally block contains a return false statement, so it returns directly and does not go back to the rest of the catch block.

Inside the try block, you can use the try block again.

try {
  try {
    consle.log('Hello world! '); / / an error
  }
  finally {
    console.log('Finally');
  }
  console.log('Will I run? ');
} catch(error) {
  console.error(error.message);
}
// Finally
// consle is not defined
Copy the code

In the code above, we have a try inside a try. The inner try gives an error (console misspelled), which executes the inner finally block and then throws an error that is caught by the outer catch.