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

我有一个脚本,我需要一个node.js脚本,我想保持JavaScript引擎独立。

所以,例如,我想要做的是:

exports.x = y; 

只有在node.js下运行 我怎样才能执行这个testing?

编辑:当发布这个问题时,我不知道node.js模块function是基于commonjs 。

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

脚本如何能够判断它是否被要求作为commonjs模块?

这是Underscore.js库如何做的(通过寻找CommonJS支持):

编辑:到您更新的问题:

 (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._ = _; } })(); 

这里的例子保留了模块模式。

那么没有可靠的方法来检测Node.js中的运行,因为每个网站可以轻松地声明相同的variables,但是,由于在默认情况下Node.js中没有window对象,你可以反过来检查你是否正在运行浏览器。

这是我用于libs应该在浏览器和Node.js下工作:

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

如果在Node.js中定义了window它可能仍然会爆炸,但是没有什么好的理由让别人这样做,因为你明确地需要忽略var或者在global对象上设置属性。

编辑

为了检测您的脚本是否被要求作为CommonJS模块,这再次不容易。 只有commonJS指定的是A:模块将通过对函数require和B的调用被包括:模块通过exports对象上的属性导出事物。 现在如何实现是留给底层系统的。 Node.js将模块内容封装在匿名函数中。

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

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

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

试图弄清楚你的代码运行在什么环境的问题是任何对象都可以被修改和声明,使得它几乎不可能找出哪些对象是环境本地的,哪些是由程序修改的。

然而,我们可以利用一些技巧来弄清楚你所处的环境。

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

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

这个技术对于服务器端来说确实很好,因为当require函数被调用时,它将this对象重置为一个空对象,并且为你重新定义module ,这意味着你不必担心任何外部篡改。 只要你的代码被加载require ,你就是安全的。

然而,这在浏览器上是分开的,因为任何人都可以很容易地定义module ,使它看起来像是你正在寻找的对象。 一方面,这可能是你想要的行为,但是它也决定了库用户可以在全局范围内使用哪些variables。 也许有人想使用一个名称module的variables,已经exports其中的另一个使用。 这是不太可能的,但是我们是谁来判断别人可以使用哪些variables,只是因为另一个环境使用了这个variables名呢?

但是,如果我们假定你的脚本被加载到全局范围内(如果它是通过脚本标签加载的话),variables不能被保留在外部的闭包中,因为浏览器不允许。 现在请记住,在节点中, this对象是一个空对象,但是modulevariables仍然可用。 那是因为它是在外部封闭中声明的。 所以我们可以通过增加一个额外的检查来修正下划线的检查:

this.module !== module

这样,如果有人在浏览器中声明全局范围内的module ,它将被放置在this对象中,这将导致testing失败,因为this.module将和模块是同一个对象。 在节点上, this.module不存在,并且module存在于一个外层闭包中,所以testing会成功,因为它们不是等价的。

因此,最后的testing是:

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

注意:虽然现在允许在全局范围内自由使用modulevariables,但是仍然可以在浏览器中创build一个新的闭包和声明module ,然后在该闭包中加载脚本。 此时用户正在完全复制节点环境,并希望知道他们在做什么,并试图做一个节点风格的要求。 如果代码在脚本标记中被调用,它将仍然是安全的,从任何新的外部closures。

下面的工作在浏览器中,除非有意,明确的破坏:

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

巴姆。

我目前偶然发现一个错误的节点检测,由于误导性的特征检测,这个节点没有意识到电子中的节点环境。 以下解决scheme明确标识了过程环境。


只能识别Node.js

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

这将发现,如果你正在运行一个节点进程,因为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) 

这个语句在节点5.5.0,电子0.36.9(使用节点5.1.1)和Chrome 48.0.2564.116进行了testing。

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

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

@达鲁格的评论激励我去思考一个更一般的certificate。 这应该从Node.js> = 0.10工作 。 我没有find以前版本的唯一标识符。


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

大多数提出的解决scheme实际上可能是伪造的。 一个可靠的方法是使用Object.prototype.toString检查全局对象的内部Class属性。 内部类不能伪装成JavaScript:

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

另一个环境检测

(意思是:这里的大部分答案都没问题。)

 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 ?.. } 

有点偏执吧? 你可以通过检查更多的全局variables来使这个更加冗长。

但是不要!

以上所有内容都可以伪造/模拟。

例如伪造global对象:

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

这不会附加到Node的原始全局对象上,但会被附加到浏览器中的window对象上。 所以这意味着你在一个浏览器中的Node env。

生命短暂!

我们关心假如我们的环境是假的吗? 当一些愚蠢的开发者在全局范围内声明一个名为global的全局variables时,会发生这种情况。 或者某个邪恶的开发者以某种方式在我们的env中注入代码。

我们可能会阻止我们的代码执行,当我们赶上这个,但我们的应用程序的很多其他依赖项可能会陷入这个。 所以最终代码将会中断。 如果你的代码足够好,你不应该关心别人可能犯的每一个愚蠢的错误。

所以呢?

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

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

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

例如

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

以下是我的变化:

 (function(publish) { "use strict"; function House(no) { this.no = no; }; House.prototype.toString = function() { return "House #"+this.no; }; publish(House); })((typeof module == 'undefined' || (typeof window != 'undefined' && this == window)) ? function(a) {this["House"] = a;} : function(a) {module.exports = a;}); 

要使用它,你可以在最后一行修改“House”作为模块的名字,然后发布你希望模块的值(通常是构造函数或者对象字面值) )。

在浏览器中,全局对象是窗口,它具有对自身的引用(有一个window.window,它是==窗口)。 在我看来,这是不太可能发生,除非你在浏览器或在一个环境,希望你相信你在浏览器。 在所有其他情况下,如果声明了全局“模块”variables,则使用该variables,否则使用全局对象。

我正在使用process来检查node.js是这样的

 if (typeof(process) !== 'undefined' && process.version === 'v0.9.9') { console.log('You are running Node.js'); } else { // check for browser } 

要么

 if (typeof(process) !== 'undefined' && process.title === 'node') { console.log('You are running Node.js'); } else { // check for browser } 

logging在这里

那么使用过程对象并检查execPath node呢?

process.execPath

这是启动进程的可执行文件的绝对path名。

例:

在/ usr / local / bin目录/节点

脚本如何能够判断它是否被要求作为commonjs模块?

相关:为了检查它是否被要求作为一个模块直接在节点运行,你可以检查require.main !== modulehttp://nodejs.org/docs/latest/api/modules.html#accessing_the_main_module

这也是一个很酷的方法:

 const isBrowser = this.window === this; 

这是有效的,因为在浏览器中,全局“this”variables有一个名为“window”的自引用。 这个自引用在Node中是不存在的。

  • 在浏览器中,“this”是对全局对象的引用,称为“窗口”。
  • 在Node中,this是对module.exports对象的引用。
    • “这个” 不是对称为“全局”的节点全局对象的引用。
    • “this” 不是对模块variables声明空间的引用。

要打破上面提到的浏览器检查,你将不得不做类似以下的事情

 this.window = this; 

在执行检查之前。

Node.js有process对象,所以只要你没有任何其他的脚本创buildprocess你可以用它来确定代码是否在Node上运行。

 var isOnNodeJs = false; if(typeof process != "undefined") { isOnNodeJs = true; } if(isOnNodeJs){ console.log("you are running under node.js"); } else { console.log("you are NOT running under node.js"); } 

这是确保服务器端和客户端JavaScript兼容的一种非常安全和直接的方式,也可以与browserify,RequireJS或CommonJS包含在客户端一起使用:

 (function(){ // `this` now refers to `global` if we're in NodeJS // or `window` if we're in the browser. }).call(function(){ return (typeof module !== "undefined" && module.exports && typeof window === 'undefined') ? global : window; }()) 

编辑 :关于你更新的问题: “一个脚本如何能告诉它是否被要求作为commonjs模块? 我不认为它可以。 你可以检查是否exports是一个对象( if (typeof exports === "object") ),因为规范要求它提供给模块,但是告诉你的是… exports是一个对象。 🙂


原始答案:

我确定有一些EventEmitter特定的符号( EventEmitter ,也许 不是,你必须使用require来获取事件模块;见下文 ),但是正如David所说的,理想情况下,最好是检测特征(而不是环境),如果这样做是有道理的。

更新 :也许是这样的:

 if (typeof require === "function" && typeof Buffer === "function" && typeof Buffer.byteLength === "function" && typeof Buffer.prototype !== "undefined" && typeof Buffer.prototype.write === "function") { 

但是,这只是告诉你,你在一个需求的环境,非常非常像NodeJS的Buffer 。 🙂

采取node.js的来源,并将其更改为定义像runningOnNodeJS这样的variables。 检查你的代码中的variables。

如果您不能拥有自己的专用版本的node.js,请在项目中打开一个function请求。 询问他们是否定义了一个variables,该variables为您提供了运行的node.js的版本,然后检查该variables。

非常旧的post,但我只是通过在try-catch中包装require语句来解决它

  try { var fs = require('fs') } catch(e) { alert('you are not in node !!!') }