如何检查脚本是否在 Node.js 下运行?

IT技术 javascript node.js commonjs
2021-01-25 05:21:09

我有一个需要来自 Node.js 脚本的脚本,我希望它保持 JavaScript 引擎独立。

例如,我只想exports.x = y;在它在 Node.js 下运行时才这样做我该如何执行此测试?


在发布这个问题时,我不知道 Node.js module功能是基于CommonJS 的

对于我给出的具体示例,更准确的问题是:

脚本如何判断它是否需要作为 CommonJS module?

6个回答

好吧,没有可靠的方法来检测 Node.js 中的运行情况,因为每个网站都可以轻松声明相同的变量,但是,由于window默认情况下 Node.js 中没有对象,您可以反过来检查您是否在内部运行浏览器。

这是我用于应该在浏览器和 Node.js 下都可以工作的库的内容:

if (typeof window === 'undefined') {
    exports.foo = {};

} else {
    window.foo = {};
}

如果它window在 Node.js 中定义,它可能仍然会爆炸,但没有充分的理由让某人这样做,因为您需要明确地省略var或设置global对象的属性

编辑

要检测您的脚本是否需要作为 CommonJS module,这又不是一件容易的事。commonJS 唯一指定的是 A:module将通过对函数的调用包含在内,require而 B:module通过exports对象上的属性导出事物现在如何实现留给底层系统。Node.js 将module的内容包装在一个匿名函数中:

function (exports, require, module, __filename, __dirname) { 

参见:https : //github.com/ry/node/blob/master/src/node.js#L325

但是不要试图通过一些疯狂的arguments.callee.toString()东西来检测它,而只是使用我上面检查浏览器的示例代码。Node.js 是一种更清洁的环境,因此不太可能window在那里声明。

@Eric 我非常怀疑它会出现在全局范围内,所以除非你window在module的第一行导入一些东西,否则你应该不会有任何问题。您还可以运行匿名函数并检查其中的[[Class]]of this(仅在非严格模式下有效)请参阅以下“类”:bonsaiden.github.com/JavaScript-Garden/#typeof
2021-03-17 05:21:09
可能......我去了另一个方向:1)检测对onhashchange(窗口中的“onhashchange”)的支持以避免创建无限循环2)通过在主node.js脚本中的模拟窗口上设置onhashchange属性来模拟支持。
2021-03-18 05:21:09
关于“Node.js 是一种更清洁的环境,因此不太可能在那里声明窗口。”:好吧,我只是来这里寻找一种方法来确定我的脚本是否在由 node.js + JSDOM 模拟的浏览器中运行或者在普通浏览器中......原因是我有一个无限循环使用 setTimeout 来检查 URL 位置,这在浏览器中很好,但是让 node.js 脚本永远运行......所以可能会有一个窗口毕竟在 node.js 脚本中:)
2021-03-23 05:21:09
typeof self === 'object'可能更安全,因为typeof window === 'undefined'在网络工作者范围内失败。
2021-03-24 05:21:09
我的问题与 OP 略有不同:我不需要脚本,它由 JSDOM 加载,模拟窗口作为全局上下文......它仍然由 node.js + V8 运行,只是在与通常module不同的上下文中。
2021-04-05 05:21:09

通过寻找 CommonJS 支持Underscore.js库是这样做的:

编辑:对您更新的问题:

(function () {

    // Establish the root object, `window` in the browser, or `global` on the server.
    var root = this; 

    // Create a reference to this
    var _ = new Object();

    var isNode = false;

    // Export the Underscore object for **CommonJS**, with backwards-compatibility
    // for the old `require()` API. If we're not in CommonJS, add `_` to the
    // global object.
    if (typeof module !== 'undefined' && module.exports) {
            module.exports = _;
            root._ = _;
            isNode = true;
    } else {
            root._ = _;
    }
})();

这里的示例保留了module模式。

@MarkMelville 可以说,这正是 OP 所要求的,因此不是问题
2021-03-22 05:21:09
这肯定不是给出的最佳答案。
2021-03-24 05:21:09
这里有一个问题,钉子“钉了它”。我正在浏览器中尝试 CommonJS,我使用的module加载器定义了 module.exports,所以这个解决方案会错误地告诉我我在 node.js 中。
2021-03-26 05:21:09
这会检测浏览器可能支持的 CommonJS 支持。
2021-04-03 05:21:09
我的措辞不好。我的意思是这个解决方案有问题。OP 可能已经接受了,但我没有。
2021-04-07 05:21:09

我目前偶然发现了错误的 Node 检测,由于误导性的特征检测,它知道Electron中的 Node 环境以下解决方案明确标识流程环境。


仅识别 Node.js

(typeof process !== 'undefined') && (process.release.name === 'node')

这将发现您是否在 Node 进程中运行,因为process.release包含“与当前 [Node-] 版本相关的元数据”。

io.js产生之后,的值process.release.name也可能变成io.js(参见process-doc)。为了正确检测 Node-ready 环境,我想你应该检查如下:

识别节点 (>= 3.0.0) 或 io.js

(typeof process !== 'undefined') &&
(process.release.name.search(/node|io.js/) !== -1)

此语句已使用 Node 5.5.0、Electron 0.36.9(使用 Node 5.1.1)和 Chrome 48.0.2564.116 进行测试。

识别节点 (>= 0.10.0) 或 io.js

(typeof process !== 'undefined') &&
(typeof process.versions.node !== 'undefined')

@daluege 的评论启发我去思考一个更一般的证明。这应该适用于 Node.js >= 0.10我没有找到以前版本的唯一标识符。


Ps:我在这里发布了这个答案,因为这个问题把我带到了这里,尽管 OP 正在寻找另一个问题的答案。

@Aaron:感谢您的提示。我找不到process.version变量的任何定义(在 react、webpack 或 react-webpack 中)。我很感激定义版本变量以将其添加到答案中的任何提示。取决于 release.node 对 node >= 3.xx 的约束
2021-03-18 05:21:09
@daluege - 感谢您的灵感。不幸的是,我没有找到低于 0.10 的证明。
2021-03-22 05:21:09
这似乎是迄今为止最可靠的方法,谢谢。虽然只适用于 >= 3.0.0 的版本。
2021-03-25 05:21:09
单线更安全: function isNodejs() { return typeof "process" !== "undefined" && process && process.versions && process.versions.node; }
2021-04-09 05:21:09
我发现采用反应的WebPack,processprocess.version束中存在,所以增加了一个额外的校验process.version,其中process.release.node在客户端是未定义但是具有节点版本在服务器侧的值
2021-04-14 05:21:09

试图弄清楚你的代码在什么环境中运行的问题在于,任何对象都可以被修改和声明,这使得几乎不可能确定哪些对象是环境本机的,哪些已经被程序修改了。

但是,我们可以使用一些技巧来确定您所处的环境。

让我们从下划线库中使用的普遍接受的解决方案开始:

typeof module !== 'undefined' && module.exports

这种技术对于服务器端实际上非常好,因为当require调用函数,它会将this对象重置为空对象,然后module再次为您重新定义,这意味着您不必担心任何外部篡改。只要您的代码加载了require,您就是安全的。

但是,这在浏览器上会失效,因为任何人都可以轻松定义module使其看起来像是您正在寻找的对象。一方面,这可能是您想要的行为,但它也决定了库用户可以在全局范围内使用哪些变量。也许有人想将一个名称module包含exports在其中的变量用于其他用途。这不太可能,但是我们凭什么来判断其他人可以使用哪些变量,仅仅因为另一个环境使用该变量名称?

然而,诀窍是,如果我们假设您的脚本是在全局范围内加载的(如果它是通过脚本标签加载的),则不能在外部闭包中保留变量,因为浏览器不允许这样做. 现在记住在节点中,this对象是一个空对象,但module变量仍然可用。那是因为它是在外部闭包中声明的。所以我们可以通过添加一个额外的检查来修复下划线的检查:

this.module !== module

有了这个,如果有人module在浏览器中的全局范围内声明,它就会被放置在this对象中,这将导致测试失败,因为this.module, 将是与module相同的对象。在节点上,this.module不存在,并且module存在于外部闭包中,因此测试将成功,因为它们不等价。

因此,最终的测试是:

typeof module !== 'undefined' && this.module !== module

注意:虽然这现在允许module在全局范围内自由使用变量,但仍然可以通过创建一个新的闭包并module在其中声明,然后在该闭包中加载脚本来绕过浏览器上的这一点在这一点上,用户正在完全复制节点环境,并希望知道他们在做什么并且正在尝试执行节点样式要求。如果代码在脚本标签中被调用,它对于任何新的外部闭包仍然是安全的。

得到,Cannot read property 'module' of undefined因为这在 mocha 测试中是未定义的,例如
2021-03-23 05:21:09
哇,感谢您清楚地解释了单衬纸每件背后的原理。
2021-03-30 05:21:09

除非有意,明确破坏,否则以下内容在浏览器中有效:

if(typeof process === 'object' && process + '' === '[object process]'){
    // is node
}
else{
    // not node
}

砰。

你有什么理由使用process+''而不是process.toString()吗?
2021-03-23 05:21:09
几乎。改用这个:Object.prototype.toString.call(process)
2021-03-23 05:21:09
var process = { toString: function () { return '[object process]'; } };
2021-03-24 05:21:09
@harmic:var process = null;会导致第二种情况失败。在 Javascript 和 Java 中,表达式'' + x产生的结果与x.toString()除非在x令人讨厌相同,前者产生"null""undefined"后者会抛出错误。
2021-03-27 05:21:09
这是这个问题的最佳答案。
2021-04-05 05:21:09