ฉันจะสร้างข้อผิดพลาดที่กำหนดเองใน JavaScript ได้อย่างไร


215

ด้วยเหตุผลบางอย่างดูเหมือนว่าการมอบหมายให้สร้างไม่ทำงานในตัวอย่างต่อไปนี้:

function NotImplementedError() { 
  Error.apply(this, arguments); 
}
NotImplementedError.prototype = new Error();

var nie = new NotImplementedError("some message");
console.log("The message is: '"+nie.message+"'")

The message is: ''เล่นนี้จะช่วยให้ แนวคิดใดเกี่ยวกับสาเหตุหรือหากมีวิธีที่ดีกว่าในการสร้างErrorคลาสย่อยใหม่ มีปัญหากับapplying Errorconstructor ที่ฉันไม่รู้หรือไม่?


อินสแตนซ์ nie ของ NotImplementedError ยืนยันทำงานหลังจากการเปลี่ยนแปลงของคุณหรือไม่ ฉันคิดว่าเพื่อให้การทำงานนี้คุณต้องกำหนด NotImplementedError.prototype.constructor อย่างชัดเจน
jayarjo

ครั้งต่อไปโปรดฉีกรหัสที่ไม่เกี่ยวข้องทั้งหมดที่ไม่จำเป็นเพื่อแสดงถึงปัญหาของคุณ นอกจากนี้ wtc คือ js.jar? จำเป็นต้องทำให้เกิดปัญหาซ้ำอีกหรือไม่
BT

2
แก้ไขคำถามนี้เพื่อให้สามารถเข้าใจได้ใน 10 วินาทีแทนที่จะเป็น 10 นาที
BT

ฉันสร้างไลบรารีการสืบทอด / คลาสที่สืบทอดมาจากประเภทข้อผิดพลาดอย่างเหมาะสม: github.com/fresheneesz/proto
BT

1
jsfiddleสำหรับคำตอบบางข้อ
เนท

คำตอบ:


194

อัปเดตรหัสของคุณเพื่อกำหนดต้นแบบให้กับ Error.prototype และอินสแตนซ์ของและการยืนยันของคุณทำงาน

function NotImplementedError(message) {
    this.name = "NotImplementedError";
    this.message = (message || "");
}
NotImplementedError.prototype = Error.prototype;

อย่างไรก็ตามฉันจะโยนวัตถุของคุณเองและตรวจสอบคุณสมบัติชื่อ

throw {name : "NotImplementedError", message : "too lazy to implement"}; 

แก้ไขตามความคิดเห็น

หลังจากที่กำลังมองหาที่แสดงความคิดเห็นและพยายามที่จะจำทำไมฉันจะกำหนดต้นแบบเพื่อการError.prototypeแทนnew Error()เช่นนิโคลัส Zakas ได้ในของเขาบทความฉันสร้างjsFiddleด้วยโค้ดด้านล่าง:

function NotImplementedError(message) {
  this.name = "NotImplementedError";
  this.message = (message || "");
}
NotImplementedError.prototype = Error.prototype;

function NotImplementedError2(message) {
  this.message = (message || "");
}
NotImplementedError2.prototype = new Error();

try {
  var e = new NotImplementedError("NotImplementedError message");
  throw e;
} catch (ex1) {
  console.log(ex1.stack);
  console.log("ex1 instanceof NotImplementedError = " + (ex1 instanceof NotImplementedError));
  console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
  console.log("ex1.name = " + ex1.name);
  console.log("ex1.message = " + ex1.message);
}

try {
  var e = new NotImplementedError2("NotImplementedError2 message");
  throw e;
} catch (ex1) {
  console.log(ex1.stack);
  console.log("ex1 instanceof NotImplementedError2 = " + (ex1 instanceof NotImplementedError2));
  console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
  console.log("ex1.name = " + ex1.name);
  console.log("ex1.message = " + ex1.message);
}

ผลลัพธ์ของคอนโซลคือสิ่งนี้

undefined
ex1 instanceof NotImplementedError = true
ex1 instanceof Error = true
ex1.name = NotImplementedError
ex1.message = NotImplementedError message
Error
    at window.onload (http://fiddle.jshell.net/MwMEJ/show/:29:34)
ex1 instanceof NotImplementedError2 = true
ex1 instanceof Error = true
ex1.name = Error
ex1.message = NotImplementedError2 message

สิ่งนี้ยืนยันว่า "ปัญหา" ที่ฉันพบคือคุณสมบัติสแต็กของข้อผิดพลาดคือหมายเลขบรรทัดที่new Error()ถูกสร้างขึ้นและไม่ใช่ตำแหน่งที่throw eเกิดขึ้น อย่างไรก็ตามอาจดีกว่าที่มีผลข้างเคียงของNotImplementedError.prototype.name = "NotImplementedError"บรรทัดที่มีผลต่อวัตถุข้อผิดพลาด

นอกจากนี้สังเกตด้วยNotImplementedError2เมื่อฉันไม่ได้ตั้ง.nameอย่างชัดเจนก็เท่ากับ "ข้อผิดพลาด" อย่างไรก็ตามตามที่ระบุไว้ในความคิดเห็นเนื่องจากรุ่นนั้นกำหนดต้นแบบเป็นnew Error()ฉันสามารถตั้งค่าNotImplementedError2.prototype.name = "NotImplementedError2"และตกลง


45
คำตอบที่ดีที่สุด แต่การรับError.prototypeโดยตรงอาจเป็นรูปแบบที่ไม่ดี ถ้าคุณต้องการที่จะเพิ่มNotImplementedError.prototype.toStringวัตถุในขณะนี้ที่จะนามแฝงError.prototype.toString- NotImplementedError.prototype = new Error()ดีกว่าที่จะทำ
cdleary

4
ฉันยังคงหลงทางในสิ่งต้นแบบทั้งหมด ทำไมในตัวอย่างของคุณคุณกำหนดชื่อให้ this.name และไม่ให้ NotImplementedError.prototype.name? คุณสามารถตอบโปรดมันสำคัญสำหรับความเข้าใจของฉัน :)
jayarjo

27
ตามcode.google.com/p/chromium/issues/detail?id=228909 subclass.prototype = new Error()เป็นรูปแบบที่ไม่ดี คุณควรจะใช้subclass.prototype = Object.create(superclass.prototype)แทน ฉันหวังว่ามันอาจแก้ไขปัญหาการติดตามสแต็กได้เช่นกัน
Gili

8
เคล็ดลับง่ายๆในการรับ stacktrace ที่มีความหมายคือการสร้างข้อผิดพลาดในตัวสร้างและบันทึกสแต็ก มันจะให้ call stack ที่เหมาะสม + 1 บรรทัดสำหรับตัวสร้าง (เป็นการจ่ายที่เหมาะสม):this.stack = new Error().stack;
เมเรเดียน

6
-1; นี่มันผิด ทำNotImplementedError.prototype = Error.prototype;ไม่ได้ทำให้instanceofการรักษาNotImplementedErrorเป็นsubclassของErrorมันทำให้instanceofการรักษาพวกเขาเป็นระดับเดียวกันแน่นอน หากคุณวางโค้ดด้านบนลงในคอนโซลของคุณและลองใช้new Error() instanceof NotImplementedErrorคุณจะได้รับtrueซึ่งผิดอย่างชัดเจน
Mark Amery

87

คำตอบทั้งหมดข้างต้นนั้นแย่มากจริงๆ แม้แต่คนเดียวที่มี 107 ups! คำตอบที่แท้จริงคือที่นี่พวก:

การรับจากวัตถุข้อผิดพลาด - คุณสมบัติข้อความอยู่ที่ไหน

TL; DR:

A. เหตุผลที่messageไม่ได้ถูกตั้งค่าคือErrorฟังก์ชั่นที่ส่งกลับวัตถุข้อผิดพลาดใหม่และไม่ได้จัดการthisในทางใดทางหนึ่ง

B. วิธีการทำเช่นนี้คือการส่งคืนผลลัพธ์ของตัวสร้างจากตัวสร้างรวมทั้งตั้งค่าต้นแบบในวิธีที่ซับซ้อนตามปกติของจาวาสคริปต์:

function MyError() {
    var temp = Error.apply(this, arguments);
    temp.name = this.name = 'MyError';
    this.message = temp.message;
    if(Object.defineProperty) {
        // getter for more optimizy goodness
        /*this.stack = */Object.defineProperty(this, 'stack', { 
            get: function() {
                return temp.stack
            },
            configurable: true // so you can change it if you want
        })
    } else {
        this.stack = temp.stack
    }
}
//inherit prototype using ECMAScript 5 (IE 9+)
MyError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: MyError,
        writable: true,
        configurable: true
    }
});

var myError = new MyError("message");
console.log("The message is: '" + myError.message + "'"); // The message is: 'message'
console.log(myError instanceof Error); // true
console.log(myError instanceof MyError); // true
console.log(myError.toString()); // MyError: message
console.log(myError.stack); // MyError: message \n 
// <stack trace ...>


 
//for EMCAScript 4 or ealier (IE 8 or ealier), inherit prototype this way instead of above code:
/*
var IntermediateInheritor = function() {};
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor();
*/

คุณอาจจะทำกลอุบายบางอย่างที่จะระบุถึงทุกคุณสมบัติที่ไม่นับของtmpข้อผิดพลาดในการตั้งพวกเขามากกว่าอย่างชัดเจนการตั้งค่าเฉพาะstackและmessageแต่กลอุบายที่ไม่ได้รับการสนับสนุนในเช่น <9


2
วิธีนี้ยังใช้งานได้สำหรับการสร้างข้อผิดพลาดที่กำหนดเองด้วยข้อผิดพลาดที่มีอยู่ หากคุณกำลังใช้ห้องสมุดบุคคลที่สามและต้องการตัดข้อผิดพลาดที่มีอยู่กับประเภทที่กำหนดเองของคุณเองวิธีการอื่นจะทำงานไม่ถูกต้อง FYI คุณสามารถยกตัวอย่างข้อผิดพลาดวานิลลาโดยส่งข้อผิดพลาดที่มีอยู่
Kyle Mueller

1
คุณไม่ควรreturn thisอยู่ในคอนสตรัคเตอร์
Onur Yıldırım

13
ฉันลดความซับซ้อนและปรับปรุงวิธีการนี้เล็กน้อย: jsbin.com/rolojuhuya/1/edit?js,console
Matt Kantor

3
@MattKantor อาจตอบได้หรือไม่ ฉันคิดว่าฉันชอบคุณที่สุด
mpoisot

2
แทนการที่คุณสามารถทำได้temp.name = this.name = 'MyError' temp.name = this.name = this.constructor.nameด้วยวิธีนี้มันจะทำงานสำหรับคลาสย่อยของMyErrorเช่นกัน
Jo Liss

45

ใน ES2015 คุณสามารถใช้classเพื่อทำสิ่งนี้อย่างหมดจด:

class NotImplemented extends Error {
  constructor(message = "", ...args) {
    super(message, ...args);
    this.message = message + " has not yet been implemented.";
  }
}

นี้ไม่ได้ปรับเปลี่ยนโลกErrorต้นแบบช่วยให้คุณสามารถปรับแต่งmessage, nameและคุณลักษณะอื่น ๆ และถูกจับสแต็ค นอกจากนี้ยังสามารถอ่านได้สวย

แน่นอนคุณอาจต้องใช้เครื่องมือเช่นbabelถ้ารหัสของคุณจะทำงานบนเบราว์เซอร์รุ่นเก่า


23

หากใครอยากรู้วิธีสร้างข้อผิดพลาดที่กำหนดเองและรับการติดตามสแต็ก:

function CustomError(message) {
  this.name = 'CustomError';
  this.message = message || '';
  var error = new Error(this.message);
  error.name = this.name;
  this.stack = error.stack;
}
CustomError.prototype = Object.create(Error.prototype);

try {
  throw new CustomError('foobar');
}
catch (e) {
  console.log('name:', e.name);
  console.log('message:', e.message);
  console.log('stack:', e.stack);
}

7

มาตรฐานส่วนนี้อาจอธิบายได้ว่าทำไมการError.applyโทรไม่เริ่มต้นวัตถุ:

15.11.1 ตัวสร้างข้อผิดพลาดที่เรียกว่าเป็นฟังก์ชัน

เมื่อข้อผิดพลาดถูกเรียกว่าเป็นฟังก์ชั่นแทนที่จะเป็นตัวสร้างมันจะสร้างและเริ่มต้นวัตถุข้อผิดพลาดใหม่ ดังนั้นการเรียกใช้ฟังก์ชันข้อผิดพลาด (... ) เทียบเท่ากับนิพจน์การสร้างวัตถุใหม่ข้อผิดพลาด (... ) ที่มีอาร์กิวเมนต์เดียวกัน

ในกรณีนี้Errorฟังก์ชั่นอาจเป็นตัวกำหนดว่ามันไม่ได้ถูกเรียกว่าเป็นตัวสร้างดังนั้นมันจะส่งคืนอินสแตนซ์ข้อผิดพลาดใหม่แทนที่จะเริ่มต้นthisวัตถุ

การทดสอบด้วยรหัสต่อไปนี้ดูเหมือนจะแสดงให้เห็นว่านี่เป็นสิ่งที่เกิดขึ้นจริง:

function NotImplementedError() { 
   var returned = Error.apply(this, arguments);
   console.log("returned.message = '" + returned.message + "'");
   console.log("this.message = '" + this.message + "'");
}
NotImplementedError.prototype = new Error();

var nie = new NotImplementedError("some message");

เอาต์พุตต่อไปนี้ถูกสร้างขึ้นเมื่อรันนี้:

returned.message = 'some message'
this.message = ''

สิ่งนี้จะถูกจำลองด้วยคลาสข้อผิดพลาดที่กำหนดเองได้อย่างไร ตัวอย่างเช่นคลาสข้อผิดพลาดที่กำหนดเองของฉันสามารถใช้เป็นทั้งฟังก์ชันที่สร้างอินสแตนซ์และเป็นตัวสร้างได้อย่างไร
Lea Hayes

ไม่นี่ไม่เป็นความจริง หากมันส่งคืนอินสแตนซ์ข้อผิดพลาดใหม่คุณสมบัติของผงชูรสของเขาจะทำงาน
BT

@BT คุณสมบัติของผงชูรสบนอินสแตนซ์ใหม่ส่งผลต่อคุณสมบัติของผงชูรสthisในมีError.apply(this, arguments);อะไรบ้าง? ฉันกำลังบอกว่าการเรียกใช้ข้อผิดพลาดที่นี่กำลังสร้างวัตถุใหม่ซึ่งถูกโยนทิ้งไป nieไม่ได้เตรียมใช้วัตถุที่สร้างขึ้นแล้วซึ่งได้รับมอบหมายให้
Dave

@ BT ฉันได้เพิ่มโค้ดตัวอย่างบางส่วนที่หวังว่าจะทำให้ชัดเจนยิ่งขึ้นในสิ่งที่ฉันพยายามจะพูด
เดฟ

@Dave ฉันอาจเข้าใจผิดวัตถุประสงค์ที่นี่ แต่NotImplementedErrorการใช้งานของคุณไม่ควรส่งคืนreturnedตัวแปรหรือไม่
blong

7
function InvalidValueError(value, type) {
    this.message = "Expected `" + type.name + "`: " + value;
    var error = new Error(this.message);
    this.stack = error.stack;
}
InvalidValueError.prototype = new Error();
InvalidValueError.prototype.name = InvalidValueError.name;
InvalidValueError.prototype.constructor = InvalidValueError;

3
นี่คือคำตอบที่ดีที่สุดที่นี่ มันสำเร็จและข้อยกเว้นที่สร้างขึ้นด้วยวิธีนี้จะทำงานได้อย่างถูกต้องในทุกสถานการณ์ นอกจากนี้ยังรักษาการสแต็กติดตามซึ่งเป็นสิ่งสำคัญมากในการใช้งานที่ไม่สำคัญ ฉันจะแทนที่ "prototype = new Error ()" ด้วย "prototype = Object.create (Error.prototype)" สำหรับ Node.js มีห้องสมุดขนาดเล็กที่ทำสิ่งนี้ให้คุณ: npmjs.com/package/node-custom-errors
Lukasz Korzybski

6

ฉันมีปัญหาที่คล้ายกันนี้ ข้อผิดพลาดของฉันต้องเป็นinstanceofทั้งErrorและNotImplementedและยังต้องสร้าง backtrace ที่ต่อเนื่องกันในคอนโซล

ทางออกของฉัน:

var NotImplemented = (function() {
  var NotImplemented, err;
  NotImplemented = (function() {
    function NotImplemented(message) {
      var err;
      err = new Error(message);
      err.name = "NotImplemented";
      this.message = err.message;
      if (err.stack) this.stack = err.stack;
    }
    return NotImplemented;
  })();
  err = new Error();
  err.name = "NotImplemented";
  NotImplemented.prototype = err;

  return NotImplemented;
}).call(this);

// TEST:
console.log("instanceof Error: " + (new NotImplemented() instanceof Error));
console.log("instanceof NotImplemented: " + (new NotImplemented() instanceofNotImplemented));
console.log("message: "+(new NotImplemented('I was too busy').message));
throw new NotImplemented("just didn't feel like it");

ผลลัพธ์ของการรันด้วย node.js:

instanceof Error: true
instanceof NotImplemented: true
message: I was too busy

/private/tmp/t.js:24
throw new NotImplemented("just didn't feel like it");
      ^
NotImplemented: just didn't feel like it
    at Error.NotImplemented (/Users/colin/projects/gems/jax/t.js:6:13)
    at Object.<anonymous> (/Users/colin/projects/gems/jax/t.js:24:7)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.runMain (module.js:487:10)
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)

ข้อผิดพลาดผ่านเกณฑ์ทั้งสามของฉันและถึงแม้ว่าstackคุณสมบัตินั้นจะไม่ได้มาตรฐาน แต่ก็ได้รับการสนับสนุนในเบราว์เซอร์ที่ใหม่กว่าซึ่งส่วนใหญ่ยอมรับได้ในกรณีของฉัน


5

Accoring to Joyentคุณไม่ควรยุ่งกับคุณสมบัติกองซ้อน (ซึ่งฉันเห็นในคำตอบมากมายที่ให้ไว้ที่นี่) เพราะมันจะส่งผลเสียต่อประสิทธิภาพการทำงาน นี่คือสิ่งที่พวกเขาพูดว่า:

กอง:โดยทั่วไปไม่ยุ่งกับสิ่งนี้ อย่าแม้แต่จะเพิ่มมัน V8 คำนวณเฉพาะในกรณีที่มีคนอ่านคุณสมบัติซึ่งปรับปรุงประสิทธิภาพอย่างมากสำหรับข้อผิดพลาดที่จัดการได้ หากคุณอ่านคุณสมบัติเพียงเพื่อเพิ่มคุณจะได้รับการชำระค่าใช้จ่ายแม้ว่าผู้โทรของคุณไม่ต้องการสแต็ค

ฉันชอบและอยากจะพูดถึงความคิดของพวกเขาในการห่อข้อผิดพลาดเดิมซึ่งเป็นการแทนที่ที่ดีสำหรับการส่งผ่านสแต็ค

ดังนั้นนี่คือวิธีที่ฉันสร้างข้อผิดพลาดที่กำหนดเองโดยพิจารณาจากที่กล่าวถึงข้างต้น:

รุ่น es5:

function RError(options) {
    options = options || {}; // eslint-disable-line no-param-reassign
    this.name = options.name;
    this.message = options.message;
    this.cause = options.cause;

    // capture stack (this property is supposed to be treated as private)
    this._err = new Error();

    // create an iterable chain
    this.chain = this.cause ? [this].concat(this.cause.chain) : [this];
}
RError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: RError,
        writable: true,
        configurable: true
    }
});

Object.defineProperty(RError.prototype, 'stack', {
    get: function stack() {
        return this.name + ': ' + this.message + '\n' + this._err.stack.split('\n').slice(2).join('\n');
    }
});

Object.defineProperty(RError.prototype, 'why', {
    get: function why() {
        var _why = this.name + ': ' + this.message;
        for (var i = 1; i < this.chain.length; i++) {
            var e = this.chain[i];
            _why += ' <- ' + e.name + ': ' + e.message;
        }
        return _why;
    }
});

// usage

function fail() {
    throw new RError({
        name: 'BAR',
        message: 'I messed up.'
    });
}

function failFurther() {
    try {
        fail();
    } catch (err) {
        throw new RError({
            name: 'FOO',
            message: 'Something went wrong.',
            cause: err
        });
    }
}

try {
    failFurther();
} catch (err) {
    console.error(err.why);
    console.error(err.stack);
    console.error(err.cause.stack);
}

รุ่น es6:

class RError extends Error {
    constructor({name, message, cause}) {
        super();
        this.name = name;
        this.message = message;
        this.cause = cause;
    }
    [Symbol.iterator]() {
        let current = this;
        let done = false;
        const iterator = {
            next() {
                const val = current;
                if (done) {
                    return { value: val, done: true };
                }
                current = current.cause;
                if (!val.cause) {
                    done = true;
                }
                return { value: val, done: false };
            }
        };
        return iterator;
    }
    get why() {
        let _why = '';
        for (const e of this) {
            _why += `${_why.length ? ' <- ' : ''}${e.name}: ${e.message}`;
        }
        return _why;
    }
}

// usage

function fail() {
    throw new RError({
        name: 'BAR',
        message: 'I messed up.'
    });
}

function failFurther() {
    try {
        fail();
    } catch (err) {
        throw new RError({
            name: 'FOO',
            message: 'Something went wrong.',
            cause: err
        });
    }
}

try {
    failFurther();
} catch (err) {
    console.error(err.why);
    console.error(err.stack);
    console.error(err.cause.stack);
}

ฉันได้ใส่โซลูชันลงในโมดูลนี่คือ: https://www.npmjs.com/package/rerror


3

ฉันชอบที่จะทำสิ่งนี้:

  • ใช้ประโยชน์จากชื่อเพื่อ toString () พ่น"{code}: {message}"
  • ส่งคืนสิ่งเดียวกันกลับสู่ super ดังนั้นจึงปรากฏเหมือนกันใน stacktrace
  • แนบรหัสเพื่อerror.codeเป็นการตรวจสอบ / แยกรหัสเป็นรหัสที่ดีกว่าการตรวจสอบข้อความซึ่งคุณอาจต้องการ จำกัด วงตัวอย่างเช่น
  • แนบข้อความเพื่อerror.messageเป็นทางเลือกerror.toString()

class AppException extends Error {
  constructor(code, message) {
    const fullMsg = message ? `${code}: ${message}` : code;
    super(fullMsg);
    this.name = code;
    this.code = code;
    this.message = fullMsg;
  }
  
  toString() {
    return this.message;
  }
}

// Just a code
try {
  throw new AppException('FORBIDDEN');
} catch(e) {
  console.error(e);
  console.error(e.toString());
  console.log(e.code === 'FORBIDDEN');
}

// A code and a message
try {
  throw new AppException('FORBIDDEN', 'You don\'t have access to this page');
} catch(e) {
  console.error(e);
  console.error(e.toString());
  console.log(e.code === 'FORBIDDEN');
}


2

ฉันต้องใช้สิ่งนี้และพบว่าสแต็คหายไปในการใช้งานข้อผิดพลาดของฉันเอง สิ่งที่ฉันต้องทำคือสร้างข้อผิดพลาดแบบจำลองและดึงสแต็กจากนั้น:

My.Error = function (message, innerException) {
    var err = new Error();
    this.stack = err.stack; // IMPORTANT!
    this.name = "Error";
    this.message = message;
    this.innerException = innerException;
}
My.Error.prototype = new Error();
My.Error.prototype.constructor = My.Error;
My.Error.prototype.toString = function (includeStackTrace) {
    var msg = this.message;
    var e = this.innerException;
    while (e) {
        msg += " The details are:\n" + e.message;
        e = e.innerException;
    }
    if (includeStackTrace) {
        msg += "\n\nStack Trace:\n\n" + this.stack;
    }
    return msg;
}

สิ่งนี้ไม่ได้ตั้งค่าข้อความ
BT

2

ฉันใช้รูปแบบตัวสร้างเพื่อสร้างวัตถุข้อผิดพลาดใหม่ ฉันกำหนดห่วงโซ่ต้นแบบเช่นErrorอินสแตนซ์ ดูการอ้างอิงตัวสร้างข้อผิดพลาด MDN

คุณสามารถตรวจสอบตัวอย่างนี้ในส่วนสำคัญนี้

การดำเนินงาน

// Creates user-defined exceptions
var CustomError = (function() {
  'use strict';

  //constructor
  function CustomError() {
    //enforces 'new' instance
    if (!(this instanceof CustomError)) {
      return new CustomError(arguments);
    }
    var error,
      //handles the arguments object when is passed by enforcing a 'new' instance
      args = Array.apply(null, typeof arguments[0] === 'object' ? arguments[0] : arguments),
      message = args.shift() || 'An exception has occurred';

    //builds the message with multiple arguments
    if (~message.indexOf('}')) {
      args.forEach(function(arg, i) {
        message = message.replace(RegExp('\\{' + i + '}', 'g'), arg);
      });
    }

    //gets the exception stack
    error = new Error(message);
    //access to CustomError.prototype.name
    error.name = this.name;

    //set the properties of the instance
    //in order to resemble an Error instance
    Object.defineProperties(this, {
      stack: {
        enumerable: false,
        get: function() { return error.stack; }
      },
      message: {
        enumerable: false,
        value: message
      }
    });
  }

  // Creates the prototype and prevents the direct reference to Error.prototype;
  // Not used new Error() here because an exception would be raised here,
  // but we need to raise the exception when CustomError instance is created.
  CustomError.prototype = Object.create(Error.prototype, {
    //fixes the link to the constructor (ES5)
    constructor: setDescriptor(CustomError),
    name: setDescriptor('JSU Error')
  });

  function setDescriptor(value) {
    return {
      configurable: false,
      enumerable: false,
      writable: false,
      value: value
    };
  }

  //returns the constructor
  return CustomError;
}());

การใช้งาน

ตัวสร้าง CustomError สามารถรับข้อโต้แย้งมากมายเพื่อสร้างข้อความเช่น

var err1 = new CustomError("The url of file is required"),
    err2 = new CustomError("Invalid Date: {0}", +"date"),
    err3 = new CustomError("The length must be greater than {0}", 4),
    err4 = new CustomError("Properties .{0} and .{1} don't exist", "p1", "p2");

throw err4;

และนี่คือลักษณะข้อผิดพลาดที่กำหนดเอง:

ห่วงโซ่ต้นแบบข้อผิดพลาดที่กำหนดเอง


คนที่ลงคะแนนคุณมีข้อโต้แย้งหรือเหตุผลในการลงคะแนนหรือไม่? หรือเพียงแค่ไม่เข้าใจความตั้งใจในรหัส
jherax

ฉันเพิ่งสังเกตเห็นว่าฉันต้องคลิกปุ่ม downvote โดยไม่ตั้งใจขณะเรียกดูหน้านี้โดยไม่ทราบ (อาจเป็นการเรียกดูจากโทรศัพท์ของฉัน) ฉันสังเกตเห็นเพียงแค่วันนี้ในขณะที่เรียกดูประวัติของฉัน มันไม่ได้ตั้งใจอย่างแน่นอน แต่ฉันไม่สามารถยกเลิกได้เพราะมันอยู่ในช่วงเวลาผ่อนผัน คุณให้คำตอบที่ให้ข้อมูลและไม่สมควรได้รับแน่นอน หากคุณทำการแก้ไขฉันจะเลิกทำ downvote อย่างมีความสุข ขอโทษด้วยกับเรื่องนั้น.
jschr

1

นวกรรมิกจะต้องเป็นเหมือนวิธีการของโรงงานและคืนสิ่งที่คุณต้องการ หากคุณต้องการวิธีการ / คุณสมบัติเพิ่มเติมคุณสามารถเพิ่มพวกเขาไปยังวัตถุก่อนส่งคืน

function NotImplementedError(message) { return new Error("Not implemented", message); }

x = new NotImplementedError();

แม้ว่าฉันจะไม่แน่ใจว่าทำไมคุณต้องทำเช่นนี้ ทำไมไม่ใช้เพียงnew Error...? ข้อยกเว้นที่กำหนดเองไม่ได้เพิ่ม JavaScript มากนัก (หรืออาจเป็นภาษาที่ไม่ได้พิมพ์)


2
คุณต้องเปิดข้อผิดพลาดประเภทลำดับชั้นหรือค่าวัตถุใน JavaScript เนื่องจากคุณสามารถระบุบล็อก catch เดียวเท่านั้น ในโซลูชันของคุณ (x instanceof NotImplementedError) เป็นเท็จซึ่งไม่เป็นที่ยอมรับในกรณีของฉัน
cdleary

1

สิ่งนี้ถูกนำไปใช้อย่างดีใน Cesium DeveloperError:

ในรูปแบบที่เรียบง่าย:

var NotImplementedError = function(message) {
    this.name = 'NotImplementedError';
    this.message = message;
    this.stack = (new Error()).stack;
}

// Later on...

throw new NotImplementedError();

วิธีนี้ใช้งานได้ดียกเว้นว่าสแต็กจะมีบรรทัดเพิ่มเติมสำหรับตัวสร้างข้อผิดพลาดซึ่งอาจเป็นปัญหาได้
SystemParadox

นอกจากนี้ไม่ผ่านการerror instanceof Errorทดสอบซึ่งจะเป็นประโยชน์
ลอเรน

1

นี่คือการดำเนินการของฉัน:

class HttpError extends Error {
  constructor(message, code = null, status = null, stack = null, name = null) {
    super();
    this.message = message;
    this.status = 500;

    this.name = name || this.constructor.name;
    this.code = code || `E_${this.name.toUpperCase()}`;
    this.stack = stack || null;
  }

  static fromObject(error) {
    if (error instanceof HttpError) {
      return error;
    }
    else {
      const { message, code, status, stack } = error;
      return new ServerError(message, code, status, stack, error.constructor.name);
    }
  }

  expose() {
    if (this instanceof ClientError) {
      return { ...this };
    }
    else {
      return {
        name: this.name,
        code: this.code,
        status: this.status,
      }
    }
  }
}

class ServerError extends HttpError {}

class ClientError extends HttpError { }

class IncorrectCredentials extends ClientError {
  constructor(...args) {
    super(...args);
    this.status = 400;
  }
}

class ResourceNotFound extends ClientError {
  constructor(...args) {
    super(...args);
    this.status = 404;
  }
}

ตัวอย่างการใช้งาน # 1:

app.use((req, res, next) => {
  try {
    invalidFunction();
  }
  catch (err) {
    const error = HttpError.fromObject(err);
    return res.status(error.status).send(error.expose());
  }
});

ตัวอย่างการใช้งาน # 2:

router.post('/api/auth', async (req, res) => {
  try {
    const isLogged = await User.logIn(req.body.username, req.body.password);

    if (!isLogged) {
      throw new IncorrectCredentials('Incorrect username or password');
    }
    else {
      return res.status(200).send({
        token,
      });
    }
  }
  catch (err) {
    const error = HttpError.fromObject(err);
    return res.status(error.status).send(error.expose());
  }
});

0

ด้วยค่าใช้จ่ายที่ไม่สามารถใช้งานinstanceofได้ข้อมูลต่อไปนี้จะเก็บสแต็กการติดตามดั้งเดิมและไม่ใช้เทคนิคที่ไม่ได้มาตรฐาน

// the function itself
var fixError = function(err, name) {
    err.name = name;
    return err;
}

// using the function
try {
    throw fixError(new Error('custom error message'), 'CustomError');
} catch (e) {
    if (e.name == 'CustomError')
        console.log('Wee! Custom Error! Msg:', e.message);
    else
        throw e; // unhandled. let it propagate upwards the call stack
}

สิ่งที่คุณต้องทำที่นี่เพื่อให้สามารถใช้งานอินสแตนซ์ของมันคือโยน fixError ใหม่แทนที่จะเป็นแค่ fixError
BT

@BT: ไม่ใช่fixErrorฟังก์ชั่นด้านบน การเพิ่มnewเมื่อเรียกมันจะสร้างวัตถุที่ถูกโยนทิ้งไป
TJ Crowder

โอ้ผมคิดว่าผมหมายถึงการใช้ "instanceof fixError" - แน่นอนแล้ว "instanceof ข้อผิดพลาด" จะไม่ทำงาน .. ฉันเดาว่าแย่ลง ..
BT

0

ทางเลือกอื่นอาจไม่ทำงานในสภาพแวดล้อมทั้งหมดมั่นใจได้ว่ามันทำงานใน nodejs 0.8 วิธีนี้ใช้วิธีที่ไม่ได้มาตรฐานในการปรับเปลี่ยนโพรโทโพภายใน

function myError(msg){ 
      var e = new Error(msg); 
      _this = this; 
      _this.__proto__.__proto__ = e;
}

0

หากคุณใช้ Node / Chrome ตัวอย่างต่อไปนี้จะช่วยให้คุณได้รับส่วนขยายที่ตรงตามข้อกำหนดต่อไปนี้

  • err instanceof Error
  • err instanceof CustomErrorType
  • console.log () ส่งคืน [CustomErrorType]เมื่อสร้างด้วยข้อความ
  • console.log () ส่งคืน [CustomErrorType: message]เมื่อสร้างโดยไม่มีข้อความ
  • throw / stack ให้ข้อมูล ณ จุดที่เกิดข้อผิดพลาด
  • ทำงานได้อย่างเหมาะสมที่สุดใน Node.JS และ Chrome
  • จะผ่านการตรวจสอบอินสแตนซ์ใน Chrome, Safari, Firefox และ IE 8+ แต่จะไม่มีสแต็กที่ถูกต้องนอก Chrome / Safari ฉันตกลงเพราะฉันสามารถตรวจแก้จุดบกพร่องใน chrome แต่รหัสที่ต้องใช้ประเภทข้อผิดพลาดเฉพาะจะยังคงทำงานข้ามเบราว์เซอร์ หากคุณต้องการโหนดเดียวที่คุณสามารถเอาifงบและคุณความดีกำลังจะไป

เศษเล็กเศษน้อย

var CustomErrorType = function(message) {
    if (Object.defineProperty) {
        Object.defineProperty(this, "message", {
            value : message || "",
            enumerable : false
        });
    } else {
        this.message = message;
    }

    if (Error.captureStackTrace) {
        Error.captureStackTrace(this, CustomErrorType);
    }
}

CustomErrorType.prototype = new Error();
CustomErrorType.prototype.name = "CustomErrorType";

การใช้

var err = new CustomErrorType("foo");

เอาท์พุต

var err = new CustomErrorType("foo");
console.log(err);
console.log(err.stack);

[CustomErrorType: foo]
CustomErrorType: foo
    at Object.<anonymous> (/errorTest.js:27:12)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

/errorTest.js:30
        throw err;
              ^
CustomErrorType: foo
    at Object.<anonymous> (/errorTest.js:27:12)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

0

ต่อไปนี้จะทำงานให้ฉันนำมาจากเอกสารอย่างเป็นทางการ Mozilla ข้อผิดพลาด

function NotImplementedError(message) {
    var instance = new Error(message);
    instance.name = 'NotImplementedError';

    Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
    if (Error.captureStackTrace) {
        Error.captureStackTrace(instance, NotImplementedError);
    }
    return instance;
}

NotImplementedError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
    }
});

-1

ลองต้นแบบวัตถุใหม่สำหรับแต่ละอินสแตนซ์ของชนิดข้อผิดพลาดที่ผู้ใช้กำหนด จะช่วยให้การinstanceofตรวจสอบการทำงานตามปกติรวมทั้งประเภทและข้อความมีรายงานอย่างถูกต้องใน Firefox และ V8 (Chome, nodejs)

function NotImplementedError(message){
    if(NotImplementedError.innercall===undefined){
        NotImplementedError.innercall = true;
        NotImplementedError.prototype = new Error(message);
        NotImplementedError.prototype.name = "NotImplementedError";
        NotImplementedError.prototype.constructor = NotImplementedError;

        return new NotImplementedError(message);
    }
    delete NotImplementedError.innercall;
}

โปรดทราบว่ารายการเพิ่มเติมจะนำหน้าสแต็กที่ถูกต้องมาก่อน


ใช้งานไม่ได้ ลอง: var a = new NotImplementedError('a'), b = new NotImplementedError('b');. ตอนนี้a instanceof NotImplementedError == falseและb instanceof NotImplementedError == true
jjrv

-1

นี่เป็นวิธีที่เร็วที่สุดที่จะทำ:

    let thisVar = false

    if (thisVar === false) {
            throw new Error("thisVar is false. It should be true.")
    }

-3

วิธีที่ง่ายกว่า คุณสามารถทำให้วัตถุของคุณสืบทอดมาจากวัตถุข้อผิดพลาด ตัวอย่าง:

function NotImplementError(message)
{
    this.message = message;
    Error.call();
    Error.call(message);
} 

สิ่งที่เรากำลังทำคือการใช้ function call () ซึ่งเรียกนวกรรมิกของคลาส Error ดังนั้นโดยพื้นฐานแล้วจะเหมือนกับการใช้การสืบทอดคลาสในภาษาเชิงวัตถุอื่น ๆ


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.