ตรวจสอบข้อผิดพลาด typeof ใน JS


153

ใน JS ดูเหมือนว่าเป็นไปไม่ได้ที่จะตรวจสอบว่าข้อโต้แย้งที่ส่งผ่านไปยังฟังก์ชั่นนั้นเป็นประเภท 'ข้อผิดพลาด' หรืออินสแตนซ์ของข้อผิดพลาดหรือไม่

ตัวอย่างเช่นสิ่งนี้ไม่ถูกต้อง:

typeof err === 'error'

เนื่องจากมีเพียง 6 ประเภทที่เป็นไปได้ (ในรูปแบบของสตริง):

ตัวดำเนินการ typeof ส่งคืนข้อมูลชนิดเป็นสตริง มีค่าที่เป็นไปได้หกค่าที่typeofส่งคืน:

"number", "string", "boolean", "object", "function" และ "undefined"

MSDN

แต่ถ้าฉันมีกรณีใช้งานง่าย ๆ แบบนี้:

function errorHandler(err) {

    if (typeof err === 'error') {
        throw err;
    }
    else {
        console.error('Unexpectedly, no error was passed to error handler. But here is the message:',err);
    }
}

ดังนั้นวิธีที่ดีที่สุดในการตรวจสอบว่าอาร์กิวเมนต์เป็นตัวอย่างของข้อผิดพลาดอะไร

เป็นinstanceofผู้ประกอบการของความช่วยเหลือใด ๆ


10
ใช่ใช้err instanceof Error
colecmc

2
@colecmc จะไม่เป็นปัญหาหากข้อผิดพลาดอาจมาจากรหัสในเฟรมหรือหน้าต่างอื่นหรือไม่ ในกรณีนั้นจะมีวัตถุข้อผิดพลาดต้นแบบที่แตกต่างกันและอินสแตนซ์ของมันจะไม่ทำงานตามที่คาดไว้ (ดูdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/
......

@ เข้าฉันไม่คิดอย่างนั้น มันจะตรวจสอบวัตถุข้อผิดพลาดที่เกิดขึ้นจริง
colecmc

1
@colecmc ผมคิดว่าคุณจะพบว่าถ้าคุณได้โยนerrใน (พูด) ใน iframe (err instanceof Error) === falseแต่แล้วผ่านมันไปยังหน้าต่างหลักสำหรับการมอบคุณจะได้รับ นั่นเป็นเพราะ iframe และหน้าต่างหลักมีErrorต้นแบบวัตถุที่แตกต่างและแตกต่างกัน ในทำนองเดียวกันเช่นวัตถุobj = {};ประสงค์เมื่อส่งผ่านไปยังฟังก์ชั่นการทำงานในหน้าต่างที่แตกต่างกัน, (obj instanceof Object)===falseคราก (ถึงแม้จะเลวร้ายที่สุดใน IE ถ้าคุณเก็บการอ้างอิงถึง obj หลังจากที่หน้าต่างถูกทำลายหรือสำรวจพยายามที่จะโทร FNS วัตถุต้นแบบเหมือนobj.hasOwnProperty()จะโยนข้อผิดพลาด!)
Doin

คุณจะตรวจสอบประเภทของข้อผิดพลาดได้อย่างไร เช่นcatch((err)=>{if (err === ENOENT)ส่งกลับENOENT is not definedข้อผิดพลาด?
oldboy

คำตอบ:


261

คุณสามารถใช้instanceofโอเปอเรเตอร์ (แต่ดูข้อแม้ด้านล่าง!)

var myError = new Error('foo');
myError instanceof Error // true
var myString = "Whatever";
myString instanceof Error // false

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

if (myError && myError.stack && myError.message) {
  // it's an error, probably
}

อย่างไรก็ตามการพิมพ์เป็ดอาจสร้างผลบวกปลอมหากคุณมีวัตถุที่ไม่ใช่ข้อผิดพลาดที่มีstackและmessageคุณสมบัติ


1
@ AurélienRibonมันใช้ได้ดีกับReferenceErrorอินสแตนซ์ คุณกำลังตรวจสอบReferenceErrorวัตถุทั่วโลก ลองนี้มันบันทึกtry { foo } catch (e) { console.log(e instanceof Error) } trueหากคุณพยายามตรวจสอบReferenceErrorวัตถุส่วนกลางด้วยเหตุผลบางอย่างคุณสามารถใช้Error.isPrototypeOf(ReferenceError)
Trott

1
@ AurélienRibon (ใช่ยังไม่แน่ใจว่าคุณกำลังพูดว่า "สิ่งนี้จะไม่ทำงานกับ ReferenceError" ซึ่งไม่ถูกต้องหรือถ้าคุณเพิ่งชี้ให้เห็น "สิ่งนี้จะไม่ทำงานกับวัตถุ Global ReferenceError" ซึ่งถูกต้องอย่างแน่นอนแม้ว่าฉันจะมีเวลายากลำบากในการหาสถานการณ์ที่ใครจะตรวจสอบว่า แต่อาจพูดมากขึ้นเกี่ยวกับการขาดจินตนาการของฉันมากกว่าความถูกต้องของกรณีการใช้งาน)
Trott

ฮ่า ๆ เสียใจกับคนที่ผมถูกขังอยู่เพียงแค่เข้าไปในข้อผิดพลาดแปลกที่ของฉันทั้งหมดกรณีไม่ได้กรณีของReferenceError Errorนี่เป็นเพราะการเรียกไปยังvm.runInNewContext()โหนดซึ่งต้นแบบมาตรฐานทั้งหมดถูกนิยามใหม่ ผลลัพธ์ทั้งหมดของฉันไม่ใช่อินสแตนซ์ของวัตถุมาตรฐาน ผมก็มองใน SO เกี่ยวกับเรื่องนี้และลดลงในกระทู้นี้ :)
ออ Ribon

1
คุณสามารถลองconst error = (err instanceof Error || err instanceof TypeError) ? err : new Error(err.message ? err.message : err);
Aamir Afridi

คุณจะตรวจสอบว่าinstanceofเป็นประเภทข้อผิดพลาดเฉพาะได้อย่างไร
oldboy

25

ฉันถามคำถามเดิม - คำตอบของ @ Trott นั้นดีที่สุดแน่นอน

อย่างไรก็ตามด้วย JS เป็นภาษาแบบไดนามิกและมีสภาพแวดล้อมรันไทม์ของ JS จำนวนมากinstanceofผู้ประกอบการสามารถล้มเหลวโดยเฉพาะอย่างยิ่งในการพัฒนาส่วนหน้าเมื่อข้ามขอบเขตเช่น iframes ดู: https://github.com/mrdoob/three.js/issues/5886

หากคุณพอใจกับการพิมพ์เป็ดนี่น่าจะดี:

let isError = function(e){
 return e && e.stack && e.message;
}

ฉันชอบภาษาที่มีการพิมพ์แบบคงที่ส่วนบุคคล แต่ถ้าคุณใช้ภาษาแบบไดนามิกมันเป็นการดีที่สุดที่จะโอบกอดภาษาแบบไดนามิกสำหรับสิ่งที่มันเป็นมากกว่าที่จะบังคับให้ทำตัวเหมือนภาษาที่พิมพ์แบบคงที่

หากคุณต้องการให้แม่นยำมากขึ้นคุณสามารถทำสิ่งนี้ได้:

   let isError = function(e){
     return e && e.stack && e.message && typeof e.stack === 'string' 
            && typeof e.message === 'string';
    }

3
ฉันชอบรุ่นนี้เมื่อเขียนฟังก์ชันอรรถประโยชน์หรือไลบรารี มันไม่ได้ จำกัด ผู้ใช้ให้อยู่ในบริบทเดียวทั่วโลกและยังจัดการข้อผิดพลาดที่ไม่ได้ทำการ deserialized จาก JSON
snickle

ขอบคุณใช่ในหลายกรณีหากดูเหมือนว่ามีข้อผิดพลาดเราสามารถปฏิบัติต่อเหมือนข้อผิดพลาด ในบางกรณีการพิมพ์เป็ดเป็นที่ยอมรับโดยสิ้นเชิงในบางกรณีมันไม่เป็นที่ยอมรับเลยในกรณีนี้ก็ใช้ได้ดี
Alexander Mills

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

1
@Trott คุณอาจสนใจที่จะรู้เกี่ยวกับinstanceofผู้ประกอบการที่ล้มเหลวในบางกรณีดูบทความที่เชื่อมโยง
Alexander Mills

1
นี้เป็นจริงที่ง่ายที่สุดเพื่อที่จะทำงานร่วมกับประเภทที่แตกต่างกันของข้อผิดพลาด (เช่นError, ReferenceError, ... ) และในสภาพแวดล้อมของฉันinstanceofล้มเหลวอย่างน่าสังเวชในหลาย ๆ สถานการณ์
Alexis Wilke

10
var myError = new Error('foo');
myError instanceof Error // true
var myString = "Whatever";
myString instanceof Error // false

ปัญหาเฉพาะกับสิ่งนี้คือ

myError instanceof Object // true

ทางเลือกนี้จะใช้คุณสมบัตินวกรรมิก

myError.constructor === Object // false
myError.constructor === String // false
myError.constructor === Boolean // false
myError.constructor === Symbol // false
myError.constructor === Function // false
myError.constructor === Error // true

แม้ว่ามันควรจะสังเกตว่าการแข่งขันนี้มีความเฉพาะเจาะจงมากตัวอย่างเช่น:

myError.constructor === TypeError // false

2

คุณสามารถใช้Object.prototype.toStringเพื่อตรวจสอบว่าวัตถุนั้นเป็นวัตถุได้ง่ายErrorหรือไม่

function isError(obj){
    return Object.prototype.toString.call(obj) === "[object Error]";
}

function isError(obj){
    return Object.prototype.toString.call(obj) === "[object Error]";
}
console.log("Error:", isError(new Error));
console.log("RangeError:", isError(new RangeError));
console.log("SyntaxError:", isError(new SyntaxError));
console.log("Object:", isError({}));
console.log("Array:", isError([]));


1

คุณสามารถใช้ obj.constructor.name เพื่อตรวจสอบ "คลาส" ของวัตถุhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name#Function_names_in_classes

ตัวอย่างเช่น

var error = new Error("ValidationError");
console.log(error.constructor.name);

บรรทัดด้านบนจะบันทึก "ข้อผิดพลาด" ซึ่งเป็นชื่อคลาสของวัตถุ สิ่งนี้สามารถใช้กับคลาสใดก็ได้ใน javascript หากคลาสไม่ได้ใช้คุณสมบัติที่มีชื่อ "name"


สิ่งนี้ไม่น่าเชื่อถือเมื่อทำการลดขนาดรหัสและใช้คลาสย่อยข้อผิดพลาด
felixfbecker

1

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

<html>
<body >

<p>The **instanceof** operator returns true if the specified object is an instance of the specified object.</p>



<script>
	var myError = new Error("TypeError: Cannot set property 'innerHTML' of null"); // error type when element is not defined
	myError instanceof Error // true
	
	
	
	
	function test(){
	
	var v1 = document.getElementById("myid").innerHTML ="zunu"; // to change with this
	
	try {
		  var v1 = document.getElementById("myidd").innerHTML ="zunu"; // exception caught
		  } 
		  
	catch (e) {
		  if (e instanceof Error) {
			console.error(e.name + ': ' + e.message) // error will be displayed at browser console
		  }
  }
  finally{
		var v1 = document.getElementById("myid").innerHTML ="Text Changed to Zunu"; // finally innerHTML changed to this.
	}
	
	}
	
</script>
<p id="myid">This text will change</p>
<input type="button" onclick="test();">
</body>
</html>


0

หรือใช้สิ่งนี้เพื่อหาข้อผิดพลาดประเภทต่างๆ

function isError(val) {
  return (!!val && typeof val === 'object')
    && ((Object.prototype.toString.call(val) === '[object Error]')
      || (typeof val.message === 'string' && typeof val.name === 'string'))
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.