The below example of how to create a custom Error in JS can be found on MDN (link).
I am struggling to understand what is going on (specific questions below).
function CustomError(foo, message, fileName, lineNumber) {
  var instance = new Error(message, fileName, lineNumber);
  instance.foo = foo;
  Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
  if (Error.captureStackTrace) {
    Error.captureStackTrace(instance, CustomError);
  }
  return instance;
}
CustomError.prototype = Object.create(Error.prototype, {
  constructor: {
    value: Error,
    enumerable: false,
    writable: true,
    configurable: true
  }
});
if (Object.setPrototypeOf) {
  Object.setPrototypeOf(CustomError, Error);
} else {
  CustomError.__proto__ = Error;
}
try {
  throw new CustomError('baz', 'bazMessage');
} catch (e) {
  console.log(e.foo); //baz
  console.log(e.message); //bazMessage
}QUESTIONS
- Since we are returning an object inside CustomError, will using it as a constructor function (new CustomError()) and using it as a normal function object (CustomError()) yield the same outcome?
- In line 11: Do we create a new object here, instead of setting CustomError.prototypedirectly toError.prototype, so that we can extend the prototype without affecting all otherErrorobjects?
- Also in line 11: Why do we even bother setting prototypeproperty of the function, if we cannot use it as a constructor function (ref. question 1)?
- In line 4 we set the Errorinstanceto whatever called the function, right? I don't understand what the purpose is / what thethisvalue will be.
- What is the purpose of the captureStackTracecheck?
Thank you for helping me analyze and understand this snippet.
EDIT:
I wanted to add that I think I have understood the following:
- Whenever we create a new object with a constructor function (newkeyword), it gets prototype-linked to an empty object, which in turn is prototype linked toObject.prototype
- It is prototype linked to a new empty object, instead of directly to Object.prototype, because that way we can extend the prototype of the new object, without changing the behavior of all objects withObject.prototypeon its prototype chain.
- If we have two levels of "inheritance", and thus manually change the prototypeproperty of a constructor function, it should reflect the same behavior. In effect, we should set theprototypeproperty to be an empty object, which in turn is prototype linked to our newly introduced "parent"
Example:
function Person(name, gender) {
  this.name = name;
  this.gender = gender;
}
function Male(name) {
  Person.call(this, name, "male");
}
Male.prototype = Object.create(Person.prototype, {
  constructor: {
    value: Male,
    enumerable: false,
    writeable: true
  }
});
var person1 = new Male("Chris");- As seen above, when changing the prototypeproperty manually, we should not only assign a new empty object to theprototypeproperty, but also set theconstructorproperty of that empty object
- This is because every object should indeed be able to look at its prototype to figure out what object constructed it. This follows the behavior of Object.prototype, where theconstructorproperty isObject(same with other built-ins)
That should explain the second block. Have I understood that part correctly?
 
     
    