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

the*_*osp 147 javascript commonjs node.js

我有一个我需要从node.js脚本的脚本,我想保持javascript引擎独立.

所以,例如,我想做:

__PRE__

只有它在node.js下运行 我该怎么办这个测试?

编辑:发布此问题时,我不知道node.js模块功能是基于commonjs.

对于具体的例子,我提出了一个更准确的问题:

脚本如何判断它是否需要作为commonjs模块?

Ivo*_*zel 94

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

这是我用于libs的东西,它们应该在浏览器和Node.js下工作:

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

} else {
    window.foo = {};
}
Run Code Online (Sandbox Code Playgroud)

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

编辑

为了检测您的脚本是否需要作为CommonJS模块,这又不容易.commonJS指定的只有A:模块将通过对函数的调用包含在内require:B:模块通过exports对象的属性导出东西.现在如何实现是留给底层系统.Node.js将模块内容包装在匿名函数中.

function (exports, require, module, __filename, __dirname) { 
Run Code Online (Sandbox Code Playgroud)

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

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

  • 关于"Node.js是一种更清洁的环境,因此不太可能在那里声明窗口.":好吧,我刚来这里寻找一种方法来查明我的脚本是否在由node.js + JSDOM模拟的浏览器中运行或者在一个普通的浏览器中...原因是我有一个无限循环使用setTimeout检查URL位置,这在浏览器中很好,但保持node.js脚本永远运行...所以可能有一个窗口在一个node.js脚本之后:) (2认同)
  • @Eric 我非常怀疑它会出现在全局范围内,所以除非你在模块的第一行导入一些东西作为 `window`,你应该不会有任何问题。您还可以运行一个匿名函数并检查其中的 `this` 的 `[[Class]]`(仅在非严格模式下有效)请参阅以下“类”:http://bonsaiden.github.com/JavaScript-花园/#typeof (2认同)

Ros*_*oss 76

通过查找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._ = _;
    }
})();
Run Code Online (Sandbox Code Playgroud)

此处的示例保留Module模式.

  • 这可以检测浏览器可能支持的CommonJS支持. (38认同)
  • 我的措词不好.我的意思是这个解决方案存在问题.OP可能已接受它,但我不接受. (13认同)
  • 这里有一个问题,钉枪"钉它".我正在浏览器中尝试CommonJS,而我正在使用的模块加载器定义了module.exports,所以这个解决方案会错误地告诉我我在节点中. (7认同)
  • 这绝对不是最好的答案. (6认同)

Flo*_*sch 35

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


仅识别Node.js.

(typeof process !== 'undefined') && (process.release.name === 'node')
Run Code Online (Sandbox Code Playgroud)

这将发现您是否在节点进程中运行,因为process.release包含"与当前[Node-]发布相关的元数据".

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

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

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

该声明使用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')
Run Code Online (Sandbox Code Playgroud)

@daluege的评论激发了我思考更一般的证据.这应该来自Node.js> = 0.10.我没有找到以前版本的唯一标识符.


Ps:我在这里发布了答案,因为问题引导我在这里,虽然OP正在寻找一个不同问题的答案.

  • 这似乎是迄今为止最可靠的方法,谢谢。虽然只适用于 >= 3.0.0 的版本。 (2认同)
  • 我发现在bundle中存在使用react webpack,`process`和`process.version`,所以我添加了一个额外的`process.version`检查,其中`process.release.node`在客户端未定义但是有一个节点版本作为服务器端的值 (2认同)
  • 单线更安全:`function isNodejs() { return typeof "process" !== "undefined" && process && process.versions && process.versions.node; }` (2认同)

Tim*_*imE 25

试图弄清楚代码运行的环境的问题是,任何对象都可以被修改和声明,使得几乎无法确定哪些对象是环境本机的,哪些对象已被程序修改.

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

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

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

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

然而,这在浏览器上分崩离析,因为任何人都可以轻松定义module,使其看起来像是您正在寻找的对象.一方面,这可能是您想要的行为,但它也决定了库用户可以在全局范围内使用哪些变量.也许有人想使用一个变量的名称moduleexports它内部的用于其它用途.它不太可能,但是我们要判断别人可以使用哪些变量,只是因为另一个环境使用该变量名称?

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

this.module !== module

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

因此,最终的测试是:

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

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

  • 哇,谢谢你清楚地解释每个单线的背后的基本原理. (2认同)

use*_*385 20

以下是在浏览器中工作,除非故意,明确地破坏:

if(typeof process === 'object' && process + '' === '[object process]'){
    // is node
}
else{
    // not node
}
Run Code Online (Sandbox Code Playgroud)

巴姆.

  • var process = {toString:function(){return'[object process]'; }}; (3认同)
  • 几乎.请改用:`Object.prototype.toString.call(process)` (3认同)
  • @harmic:`var process = null;`会导致第二种情况失败.在Javascript和Java中,表达式'''+ x`产生与`x.toString()`相同的情况,除非```是讨厌的,前者产生`"null"`或`'undefined"`后者会抛出错误. (3认同)
  • 这是对这个问题的最佳答案。 (2认同)

小智 14

这也是一个非常酷的方式:

const isBrowser = this.window === this;
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为在浏览器中,全局'this'变量有一个名为'window'的自引用.Node中不存在此自引用.

  • 在浏览器中,"this"是对全局对象的引用,称为"window".
  • 在Node'this'中是对module.exports对象的引用.
    • 'this' 不是对Node全局对象的引用,称为'global'.
    • 'this' 不是对模块变量声明空间的引用.

要打破上面建议的浏览器检查,您必须执行以下操作

this.window = this;
Run Code Online (Sandbox Code Playgroud)

在执行检查之前.


Onu*_*rım 10

又一个环境检测:

(意思是:这里的大多数答案都没问题.)

function isNode() {
    return typeof global === 'object'
        && String(global) === '[object global]'
        && typeof process === 'object'
        && String(process) === '[object process]'
        && global === global.GLOBAL // circular ref
        // process.release.name cannot be altered, unlike process.title
        && /node|io\.js/.test(process.release.name)
        && typeof setImmediate === 'function'
        && setImmediate.length === 4
        && typeof __dirname === 'string'
        && Should I go on ?..
}
Run Code Online (Sandbox Code Playgroud)

有点偏执吗?你可以通过检查更多全局变量来使它更加冗长.

但是不要!

无论如何,以上所有这些都可以伪造/模拟.

例如假冒global对象:

global = {
    toString: function () {
        return '[object global]';
    },
    GLOBAL: global,
    setImmediate: function (a, b, c, d) {}
 };
 setImmediate = function (a, b, c, d) {};
 ...
Run Code Online (Sandbox Code Playgroud)

这不会附加到Node的原始全局对象,但它将附加到window浏览器中的对象.所以它意味着你在浏览器中的Node env中.

生命短暂!

如果我们的环境是伪造的,我们是否在意?当一些愚蠢的开发人员声明global在全局范围内调用的全局变量时,就会发生这种情况.或者某些邪恶的开发者以某种方式在我们的环境中注入代码.

当我们捕获这个时,我们可能会阻止我们的代码执行,但我们的应用程序的许多其他依赖项可能会被捕获.所以最终代码会破裂.如果你的代码足够好,你就不应该关心别人可能做过的每一个愚蠢的错误.

所以呢?

如果定位2个环境:浏览器和节点;
"use strict"; 或者只是检查windowglobal; 并明确指出在您的代码仅支持这些环境的文档中.而已!

var isBrowser = typeof window !== 'undefined'
    && ({}).toString.call(window) === '[object Window]';

var isNode = typeof global !== "undefined" 
    && ({}).toString.call(global) === '[object global]';
Run Code Online (Sandbox Code Playgroud)

如果可能的话,你的用例; 而不是环境检测; 在try/catch块中执行同步特征检测.(这些将花费几毫秒来执行).

例如

function isPromiseSupported() {
    var supported = false;
    try {
        var p = new Promise(function (res, rej) {});
        supported = true;
    } catch (e) {}
    return supported;
}
Run Code Online (Sandbox Code Playgroud)


Fab*_*obs 9

大多数提议的解决方案实际上都可以伪造.一种强大的方法是Class使用Object.prototype.toString.检查全局对象的内部属性.内部类不能在JavaScript中伪造:

var isNode = 
    typeof global !== "undefined" && 
    {}.toString.call(global) == '[object global]';
Run Code Online (Sandbox Code Playgroud)

  • 这将在browserify下恢复正常. (2认同)
  • 这很奇怪,因为`window.toString()`生成``[object Window]"` (2认同)

Cor*_*rey 5

在撰写本文时,这个答案更像是一个“即将推出”的选项,因为它利用了 JavaScript 的非常新的特性。

const runtime = globalThis.process?.release?.name || 'not node'
console.log(runtime)
Run Code Online (Sandbox Code Playgroud)

runtime值将为nodenot node

如前所述,这依赖于一些新的 JavaScript 功能。globalThis是 ECMAScript 2020 规范中的最终功能。V8 引擎支持可选链接/无效合并( 的?部分globalThis.process?.release?.name),该引擎随 Chrome 80 一起提供。截至 2020 年 4 月 8 日,此代码将在浏览器中运行,但在 Node 中将无法运行,因为 Node 13 分支使用V8 7.9.xxx。我相信 Node 14(将于 2020 年 4 月 21 日发布)应该使用 V8 8.x+。

这种方法带有健康剂量的当前限制。然而; 浏览器/节点发布的速度,它最终将是一个可靠的单线。

  • 这应该是公认的答案!顺便说一句,每个人都应该使用节点 14 (2认同)