NodeJS 为什么`instanceof`操作符在传递给库的示例上返回false?(不涉及继承)

xwbd5t1u  于 2023-04-11  发布在  Node.js
关注(0)|答案(1)|浏览(160)

我正在用TS构建一个库。这个库使用ssh2库作为依赖项。
我正在尝试创建一个函数,它可以接受ssh2配置对象或已经存在的Client示例来执行命令(这是一个简化的情况):

import { Client, ConnectConfig } from 'ssh2';

export function runCommand(params: Client | ConnectConfig) {
  if(params instanceof Client) {
    // do something
  } else {
    // create our own client
    // do something
  }
}

当我构建这个库并像这样调用函数时:

const { readFileSync } = require("fs");
const { Client } = require("ssh2");
const { runCommand } = require("myLib");
const conn = new Client();

conn
  .on("ready", () => {
    console.log("Client :: ready");
    runCommand(conn);
  })
  .connect({
    host: "example.com",
    port: 22,
    username: "me",
    privateKey: readFileSync(process.env.HOME + "/.ssh/id_ed25519"),
  });

由于某种原因,我的函数中的instanceof检查将返回false。这里到底发生了什么?是TS编译问题还是Node.js将来自我的lib依赖项和消费者代码依赖项的Client视为两个不同的类?
编译后的代码看起来像这样:

const ssh2_1 = require("ssh2");
//...
function runCommand(params) {
    if (params instanceof ssh2_1.Client) {
        // do something
    }
    else {
       // create our own client
    // do something
    }
}
56lgkhnf

56lgkhnf1#

instanceof操作符的行为中可能不明显的是,类必须在父链中 * 完全 *,在JavaScript对象 * 引用 * 的意义上。
因此,如果您导入Client类两次,它们是不同的实际对象引用,并且instanceof可能会给予“假”否定。
这很有可能是这里发生的事情:ssh2与你的库捆绑在一起,因此它会导入自己的依赖项副本。而你的应用会自己导入ssh2,这会导致一个 * 单独 * 的副本。
你有几个选择:

  • 不要将依赖项与您的库捆绑在一起(例如,使用webpack externals),让应用安装一个您的库和应用都将导入的 * 单一 * 版本;指定peerDependencies的典型情况
  • 不要使用instanceof运算符;相反,使用一些启发式方法来确定对象是否具有您需要的形状;通常检查某些属性及其类型
  • 从库中重新导出依赖项(例如,在您的示例中为Client类),并在应用中使用该依赖项(const { Client, runCommand } = require("myLib");),以便它是完全相同的副本(既然它已经捆绑,为什么不重新使用它而不是重新捆绑它呢?)

相关问题