Подтвердить что ты не робот

Использование `instanceof` на объектах, созданных с помощью конструкторов из глубоких зависимостей npm

История:

У меня есть модуль npm, в котором я использую общий код обработки ошибок, включая пользовательскую ошибку:

function CustomError () { /* ... */ }
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.constructor = CustomError;
module.exports = CustomError;

У меня есть несколько других модулей (пусть их называют 'module-a' и 'module-b'), которые зависят от модуля обработки ошибок.

У меня также есть код с функциями Bluebirds "отфильтрованный улов":

doSomething
.catch(CustomError, () => { /* ... */ });

Проблема:

После некоторой отладки, которую я обнаружил (несколько явно задним числом), ошибки, созданные в 'module-a', не являются экземплярами ошибок, созданных 'module-b'. Это связано с тем, что оба модуля имеют собственную копию JS файла, содержащего конструктор CustomError, оба из которых выполняются независимо.

Я бы предпочел не прибегать к моему текущему решению, которое в основном:

CustomError.isCustomError = e => e.constructor.toString() === CustomError.toString();

а затем:

doSomething
.then(CustomError.isCustomError, () => { /* ... */ });

Это явно непросто и развалится, если версии не синхронизируются.

Итак...

Есть ли способ убедиться, что 'module-a' и 'module-b' используют один и тот же экземпляр конструктора? Или другое, менее хрупкое решение.

4b9b3361

Ответ 1

Это тоже проблема в браузерах, когда у вас есть iframe, он получает свою собственную копию, например, конструктор Array (делая instanceof бесполезным).

Решение для пользовательского конструктора состоит в том, чтобы утинить его. Вот некоторые потенциальные решения с плюсами и минусами.

  • Проверьте имя конструктора. Pro: простой, хорошо поддержанный. Con: лучше выбрать довольно уникальное имя, чтобы избежать ложных срабатываний и забыть о его подклассификации.

  • Проверьте свойства объекта (например, имеет функцию "foo" и "bar" и "foo" - функция). Pro: в основном дурака. Минусы: хрупкие: эта проверка может быть случайным образом нарушена, если вы реорганизуете свой собственный класс ошибок, относительно дорого.

  • (рекомендуется) Добавьте свойство/метод. Вот как справляется с этим множество библиотек (например, moment.js).

Пример кода:

CustomError.prototype._isCustomError = true;
var isCustomError = function isCustomError(obj) {
  return obj instanceof CustomError || Boolean(obj._isCustomError);
};

module.exports = {
  CustomError,
  isCustomError
};

Это более или менее точно, как момент определяет, является ли данный объект объектом момента.

Ответ 2

Что вы подразумеваете под:

После некоторой отладки, которую я обнаружил (несколько явно задним числом) что ошибки, созданные в модуле-a, не являются экземплярами созданных ошибок по модулю -b.

Объект Error не может быть экземпляром другого объекта ошибки. Или вы говорите, что ошибки от module-a или module-b при выполнении чего-то вроде err instanceof CustomError возвращают разные результаты? Имея в виду, что instanceof проверяет наличие constructor.prototype в цепочке прототипов объекта, обе ошибки из этих модулей должны возвращать true по коду, который вы отправили, при повторном тестировании CustomError.

Можете ли вы показать, как вы создаете эти ошибки в этих модулях?

Это связано с тем, что оба модуля имеют собственную копию JS файла содержащий конструктор CustomError, которые запускаются независимо друг от друга.

Опять же, смущенный этим утверждением. Что вы подразумеваете под обоими модулями, имеют свою собственную копию? Давайте рассмотрим небольшой пример:

// custom-error.js
'use strict'

function CustomError () {}

CustomError.prototype = Object.create(Error.prototype)
CustomError.prototype.constructor = CustomError

module.exports = CustomError

// module-a.js
const CustomError = require('./custom-error')
const err = new CustomError(...)
module.exports = err

// module-b.js
const CustomError = require('./custom-error')
const err = new CustomError(...)
module.exports = err

// dummy file to require those
const CustomError = require('./custom-error')
const errA = require('./module-a')
const errB = require('./module-b')

Сначала оба errA и errB должны быть instanceof CustomError:

console.log(errA instanceof CustomError) // yields true
console.log(errB instanceof CustomError) // yields true

cunstructor свойство errA и errB, которое будет найдено в цепочке прототипов, должно иметь ссылку, указывающую на тот же объект функции CustomError

console.log(errA.constructor === errB.constructor) // yields true

Давайте также представим пример фильтрации:

const Promise = require('bluebird')

Promise.resolve()
.then(() => {
  throw errA
})
.catch(CustomError, err => {
  console.log('instance of CustomError catched', err)
  throw errB
})
.catch(CustomError, err => {
  console.log('instance of CustomError catched again', err)
})

Результаты:

instance of CustomError catched [Error]
instance of CustomError catched again [Error]

И последнее, что вы имеете в виду в своем примере, когда вы говорите deep npm dependencies? Это CustomError вещь - ваш модуль или сторонняя библиотека? Тот факт, что является сторонним модулем или нет, это ничего не меняет.