1

Q: My code extending Error() is not fully successful for lack of the stack trace sometimes (like here). How can I extend Error() and also get the stack trace all the time?

I've used

I've arrived at MyError() at the top of this code:

"use strict"

/**
 * MyError (sync) - descends from Error
 * @param {string} inMsg - default null. Should be a string containing information about
 *   the error.
 * Note: technique for extending Error from
 *   https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
 *     (below heading "Custom Error Types"), as observed Mon Sep 19, 2016 and Tue Oct 18,
 *     2016 (the code example under that heading changed between those dates).
 *   https://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript,
 *     as observed Tue Oct 18, 2016. No mention of copyright restrictions noted.
 */
function MyError(inMsg = null) {
    this.name = "MyError"

    if (Object.prototype.toString.call(inMsg) === "[object String]" && inMsg.length > 0)
        { this.message = inMsg }
    else { this.message = "MyError was called without a message." }

    const lastPart = new Error().stack.match(/[^\s]+$/)
    this.stack = `${ this.name } at ${ lastPart }`
}
MyError.prototype = Object.create(Error.prototype)
MyError.prototype.name = "MyError"
MyError.prototype.message = ""
MyError.prototype.constructor = MyError
MyError.prototype.toString = function() { return `${ this.name }: ${ this.message }` }

/**
 * p and p1 - functions that demonstrate a behavior
 * @param { number } n - integer
 * @throw { LocError } - if n isn't an integer
 */
function p(n) {
    if (!Number.isInteger(n)) { throw new MyError(`n must be an integer   n=${ n }`) }
    // uses MyError()
}
function p1(n) {
    if (!Number.isInteger(n)) { throw new Error(`n must be an integer   n=${ n }`) }
    // uses Error()
}

if (true) {
    console.log(p(3))
    console.log(p("t"))
}
else {
    console.log(p1(3))
    console.log(p1("t"))
}

(That code is at https://jsfiddle.net/BaldEagle/9vp0p3pw/, though I haven't figured out how to execute it there.)

My Windows 10 is current. I'm using Node v6.2.0.

At the bottom of the code are two functions. One calls Error(); the other calls MyError(). There's no other difference.

Below them is an if statement that selects which function you observe. With that if statement set to "true" (as shown), you'll see the result of MyError(). I get

        if (!Number.isInteger(n)) { throw new MyError(`n must be an integer   n=${ n }`) }
                                    ^
 MyError at (node.js:160:18)

and only that (no stack trace).

With the if statement set to "false", you'll see the result of Error(). I get

        if (!Number.isInteger(n)) { throw new Error(`n must be an integer   n=${ n }`) }
                                    ^

Error: n must be an integer   n=t
    at p1 (C:\path\file.js:38:36)

followed by 8 or 10 lines of the preferred stack trace.

Q: My code extending Error() is not fully successful for lack of the stack trace sometimes (like here). How can I extend Error() and also get the stack trace all the time?

Community
  • 1
  • 1
BaldEagle
  • 918
  • 10
  • 18
  • why don't you use `MyError.prototype = new Error();`? – Aᴍɪʀ Nov 08 '16 at 05:19
  • The direct answer: Because I'd never heard of the technique! Well done: It works, at least in part. How does it work? Are there any negatives I don't know about? I'll do more testing tomorrow. – BaldEagle Nov 08 '16 at 05:35
  • My testing detects no negatives on this technique. It flat out works in all the ways I know to test. Well done! Thanks. – BaldEagle Nov 08 '16 at 18:46
  • @Aᴍɪʀ Because that's complete nonsense? – Bergi Nov 08 '16 at 23:37
  • [That edit](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error$compare?to=1117439&from=1064924) was complete bonkers; I've reverted it. The reason why you didn't get stack traces was that `/[^\s]+$/` never really matches anything useful. – Bergi Nov 08 '16 at 23:46
  • @Bergi: So, what do you recommend to use instead? – BaldEagle Nov 09 '16 at 03:07
  • @Amir: Thanks for posting. I'm doing some work with an error derived from MyError. For it, I used SecondError.prototype = new MyError(). When SecondError throws, the error trace shows that MyError threw; it'd be better to report SecondError. So, I'm not so sure it's working all the way. – BaldEagle Nov 09 '16 at 03:12
  • Hmmm. So you can just write `const lastPart = new Error().stack;`? @Bergi, so to call the parent's constructor you use something like `Parent.call(this)`? – Aᴍɪʀ Nov 09 '16 at 03:43
  • 1
    @Aᴍɪʀ Yes, and yes, though `Error.call(this)` doesn't work because it's a native constructor. Subclassing will work properly in ES6. – Bergi Nov 09 '16 at 10:16

0 Answers0