在使用深度 npm 依赖项的构造函数创建的对象上使用 `instanceof`

问题描述 投票:0回答:3

背景:

我有一个 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' 都使用相同的构造函数实例?或者另一种不太脆弱的解决方案。

javascript node.js npm
3个回答
15
投票

这实际上也是浏览器中的一个问题,当你有一个 iframe 时,它会获取自己的副本,例如数组构造函数(使得

instanceof
无用)。

自定义构造函数的解决方案是鸭子类型。以下是一些可能的解决方案,各有利弊。

  1. 检查构造函数名称。优点:简单,支持良好。缺点:最好选择一个相当独特的名称,以避免误报并忘记对其进行子分类。

  2. 检查对象的属性(例如,同时具有“foo”和“bar”,并且“foo”是一个函数)。优点:基本上是万无一失的。缺点:脆弱:如果您重构自定义错误类,此检查可能会随机中断,相对昂贵。

  3. (推荐)添加属性/方法。这就是许多库(例如,moment.js)处理此问题的方式。

代码示例:

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

module.exports = {
  CustomError,
  isCustomError
};

这或多或少正是 moment 检测给定对象是否为 moment 对象的方式。


0
投票

如果您不希望出现冲突的类名,则可以使用简单的打字稿实现:

type ClassConstructor<T> = new (...args: any[]) => T

export function isInstanceOf<T>(obj: any, cls: ClassConstructor<T>): boolean {
  if (!obj)
    return false;

  // Traverse the prototype chain to support inheritance
  let currentProto = Object.getPrototypeOf(obj);
  while (currentProto) {
    if (currentProto.constructor.name === cls.name)
      return true;
    
    currentProto = Object.getPrototypeOf(currentProto);
  }

  return false;
}

-2
投票

你的意思是:

经过一些调试我发现(事后看来有些明显) 在“module-a”中创建的错误不是创建的错误的实例 通过“模块-b”。

错误对象不能是另一个错误对象的实例。或者您是说,在执行类似

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
是你的模块还是第 3 方库?无论是否是第 3 方模块,这一事实不应改变任何内容。
    

© www.soinside.com 2019 - 2024. All rights reserved.