перевірка на помилку типу в JS


153

У JS видається неможливим перевірити, чи аргумент, переданий функції, насправді має тип 'помилка' або примірник помилки.

Наприклад, це невірно:

typeof err === 'error'

оскільки існує лише 6 можливих типів (у вигляді рядків):

Оператор typeof повертає інформацію про тип у вигляді рядка. Існує шість можливих значень, які typeofповертаються:

"число", "рядок", "булева", "об'єкт", "функція" та "невизначено".

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/… )
Doin

@Doin, я не думаю, що так. Він просто підтвердить фактичний об'єкт помилки.
colecmc

1
@colecmc, я думаю, ви виявите, що якщо ви вкинули err(скажімо) кадр iframe, але потім передасте його до батьківського вікна для передачі, ви отримаєте (err instanceof Error) === false. Це тому, що в iframe та його батьківському вікні є чіткі, різні Errorпрототипи об'єктів. Так само об'єкт, як obj = {};буде, коли передається функції, що працює в іншому вікні, yeild (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

Вищезгадане не працюватиме, якщо помилка була викинута в інше вікно / кадр / рамку, ніж те, де відбувається перевірка. У такому випадку 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", що неправильно, або ви просто вказували "Це не працюватиме з глобальним об'єктом ReferenceError" це абсолютно правильно, хоча мені важко зіткнутися з ситуацією, коли хтось би це перевіряв, але це може сказати більше про мою відсутність фантазії, ніж обгрунтованість випадку використання).
Тротт

Ха-ха, вибачте за цих хлопців, мене просто зафіксували дивні помилки, де всі мої ReferenceErrorекземпляри не були Error. Це було пов'язано з викликом до vm.runInNewContext()вузла, де всі стандартні прототипи переосмислюються. Всі мої результати не були примірниками стандартних об'єктів. Я дивився в цьому про це і потрапив у цю тему :)
Aurelien Ribon

1
Можна спробуватиconst error = (err instanceof Error || err instanceof TypeError) ? err : new Error(err.message ? err.message : err);
Аамір Афріді

як ви перевіряєте, чи це instanceofспецифічний тип помилки Тхо?
oldboy

25

Я задав оригінальне запитання - відповідь @ Тротта, безумовно, найкраща.

Однак, оскільки JS є динамічною мовою і з тим, що існує стільки середовищ виконання JS, instanceofоператор може вийти з ладу, особливо при розробці на передньому кінці при перетині меж, таких як рамки. Дивіться: https://github.com/mrdoob/three.js/isissue/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
Я віддаю перевагу цій версії під час написання функції утиліти або бібліотеки. Це не обмежує користувачів в одному конкретному глобальному контексті, а також належним чином обробляє деріаріалізовані помилки JSON.
snickle

дякую, так, у багатьох випадках, якщо це виглядає як помилка, ми можемо трактувати як помилку. в деяких випадках типи качок цілком прийнятні, в деяких випадках взагалі неприйнятні, в цьому випадку цілком чудово.
Олександр Міллз

Я здійснив деяку налагодження за допомогою налагоджувача, і стек і повідомлення дійсно видаються єдиними двома невідродженими властивостями в екземплярах помилок.
Олександр Міллс

1
@Trott, можливо, вам буде цікаво дізнатися про помилку instanceofоператора в деяких випадках, перегляньте пов’язану статтю.
Олександр Міллс

1
Це на самому справі найпростіший , так що працює з різними типами помилок (тобто Error, ReferenceError...). І в моєму середовищі instanceofпровалиться нещасно за багатьох обставин.
Алексіс Вілке

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'))
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.