为什么“instanceof”运算符在传递给库的实例上返回 false?(不涉及继承)

Ker*_*ney 6 javascript class instanceof node.js typescript

我正在用 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
  }
}
Run Code Online (Sandbox Code Playgroud)

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

import { Client, ConnectConfig } from 'ssh2';

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

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

编译后的代码看起来像这样:

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"),
  });
Run Code Online (Sandbox Code Playgroud)

ghy*_*ybs 6

从运算符的行为中可能不明显的是,从 JavaScript 对象引用的意义上来说,instanceof该类必须完全位于父链中。

因此,如果您导入Client类两次,它们是不同的实际对象引用,并且instanceof可能会给出“假”负数。

这很可能就是这里发生的情况:ssh2与您的库捆绑在一起,因此它导入了自己的依赖项副本。而您的应用程序会ssh2自行导入,从而产生单独的副本。

您有几个选择:

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