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.prototype
directly toError.prototype
, so that we can extend the prototype without affecting all otherError
objects? - Also in line 11: Why do we even bother setting
prototype
property of the function, if we cannot use it as a constructor function (ref. question 1)? - In line 4 we set the
Error
instance
to whatever called the function, right? I don't understand what the purpose is / what thethis
value will be. - What is the purpose of the
captureStackTrace
check?
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 (
new
keyword), 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.prototype
on its prototype chain. - If we have two levels of "inheritance", and thus manually change the
prototype
property of a constructor function, it should reflect the same behavior. In effect, we should set theprototype
property 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
prototype
property manually, we should not only assign a new empty object to theprototype
property, but also set theconstructor
property 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 theconstructor
property isObject
(same with other built-ins)
That should explain the second block. Have I understood that part correctly?