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?