перевірка на помилку типу в 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.