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

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

例如,只有当 exports.x = y;在 Node.js 下运行时,我才想执行它?


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

对于我给出的具体例子,一个更准确的问题应该是:

脚本如何判断是否需要将其作为 CommonJS 模块?

109242 次浏览

编辑 : 关于你更新的问题: “脚本如何判断是否需要它作为 commjs 模块?”我不认为它可以。您可以检查 exports是否是一个对象(if (typeof exports === "object")) ,因为 说明书要求将它提供给模块,但是所有这些都告诉您... ... exports是一个对象。:-)


原答案:

我敢肯定有一些 NodeJS 特定的符号(也许是 EventEmitter 不,您必须使用 require来获取事件模块; 请参阅下面的内容) ,你可以检查,但正如大卫所说,理想情况下,你最好检测功能(而不是环境) ,如果它有任何意义这样做。

更新 : 也许是这样的:

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

但是这只是告诉您,您所处的环境使用的是 require和非常、非常类似于 NodeJS 的 Buffer。:-)

没有可靠的方法来检测在 Node.js 中运行,因为每个网站都可以很容易地声明相同的变量,然而,由于在 Node.js 中默认没有 window对象,你可以反过来检查你是否在浏览器中运行。

下面是我对应该在浏览器和 Node.js 下都能工作的库使用的方法:

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


} else {
window.foo = {};
}

如果在 Node.js 中定义了 window,它可能仍然会爆炸,但是没有 很好的原因,因为显式地需要省略 var或者设置 global对象的属性。

剪辑

要检测您的脚本是否需要作为 CommonJS 模块,同样不容易。CommonJS 指定的唯一事情是 A: 模块将通过调用函数 require包含在内,而 B: 模块通过 exports对象上的属性导出内容。现在,如何实现它就留给了底层系统。Js 将模块的内容封装在一个匿名函数中:

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

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

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

获取 node.js 的源代码并将其更改为定义一个类似于 runningOnNodeJS的变量。在代码中检查该变量。

如果您不能拥有自己的 node.js 私有版本,请在项目中打开一个特性请求。要求他们定义一个变量,这个变量会给出运行在其中的 node.js 的版本。然后检查这个变量。

如何使用进程对象并检查 ExecPath是否为 node

Process.ExecPath

这是绝对的 可执行文件的路径名 开始了这个过程。

例如:

/usr/local/bin/node

通过查找 CommonJS 支持 下划线.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 模式。

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

相关: 要检查它是否被作为模块需要,而不是直接在节点中运行,可以检查 require.main !== moduleHttp://nodejs.org/docs/latest/api/modules.html#accessing_the_main_module

以下是我对上述内容的变体:

(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.window 是 = = window)。在我看来,这种情况不太可能发生,除非你在浏览器中或者在一个希望你相信自己在浏览器中的环境中。在所有其他情况下,如果声明了一个全局“模块”变量,则使用该变量,否则使用全局对象。

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

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

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

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

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

然而,这在浏览器上是不成立的,因为任何人都可以很容易地定义 module,使它看起来像是您正在寻找的对象。一方面,这可能是您想要的行为,但是它也规定了库用户可以在全局作用域中使用哪些变量。也许有人想使用一个名为 module的变量,其中包含 exports作为另一种用途。这不太可能,但我们有什么资格去判断别人可以使用哪些变量,仅仅因为另一个环境使用了这个变量名?

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

this.module !== module

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

因此,最终的考验是:

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

注意: 虽然现在可以在全局作用域中自由使用 module变量,但是仍然可以通过创建一个新的闭包并在其中声明 module,然后在该闭包中加载脚本来绕过这个变量。此时,用户正在完全复制节点环境,希望知道自己在做什么,并尝试执行节点样式所需的操作。如果在脚本标记中调用代码,它仍然不会受到任何新的外部闭包的影响。

我使用 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
}

给你文件

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

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

除非故意、明确地破坏,否则以下内容在浏览器中可以正常工作:

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

砰。

非常老的文章,但是我通过在 try-catch 中包装 request 语句解决了这个问题

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

这是一个相当安全和直接的方式,以确保服务器端和客户端 javascript 之间的兼容性,这也将与浏览器,RequreJS 或 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;
}())

Js 有 process对象,所以只要你没有其他创建 process的脚本,你就可以用它来确定代码是否在 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");
}

我目前偶然发现一个错误的检测节点,这是 没有意识到的节点环境在 电子由于一个误导的特征检测。以下解决方案显式标识流程环境。


只识别 Node.js

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

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

在产生 Io.js之后,process.release.name的值也可以变成 io.js(参见 程序文件)。要正确检测 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开始工作。我没找到之前版本的唯一标识符。

指认小傍

(typeof process !== 'undefined') && process.isBun)

包子现时并没有实施完整的工序物件布局,因此有需要留意包子现时在这方面的局限性。


附注: 我把这个问题的答案贴在这里,因为这个问题把我引到了这里,尽管观察点正在寻找一个不同问题的答案。

又一个 环境监测:

(意思是: 这里的大多数答案都是正确的。)

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

有点偏执,对吧? 您可以通过检查更多的 全球化来使这个问题更加冗长。

但是不要。

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

例如,假冒 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的全局变量时,就会发生这种情况。或者某个邪恶的开发人员在我们的环境中注入了代码。

我们可能会阻止我们的代码执行时,我们捕捉到这一点,但我们的应用程序的许多其他依赖项可能会陷入这一点。所以最终代码会被破解。如果您的代码足够好,您就不应该关心其他人可能犯的每一个愚蠢的错误。

那又怎样?

如果针对2个环境: 浏览器和节点;
或者简单地检查 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;
}

这里也有一个很酷的方法:

const isBrowser = this.window === this;

这是因为在浏览器中,全局“ This”变量有一个称为“ window”的自引用。这个自引用在 Node 中不存在。

  • 在浏览器中,“ this”是对全局对象的引用,称为“ window”。
  • 在 Node 中,“ this”是对 module. export 的引用 对象。
    • This’是对 Node 全局对象(称为‘ global’)的 没有引用。
    • This’是 没有,是对模块变量声明空间的引用。

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

this.window = this;

在执行检查之前。

const isNode =
typeof process !== 'undefined' &&
process.versions != null &&
process.versions.node != null;

从调试包的来源:

const isBrowser = typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs

Https://github.com/visionmedia/debug/blob/master/src/index.js#l6

一行程序可以在现代 JavaScript 运行时中使用。

const runtime = globalThis.process?.release?.name || 'not node'

runtime值将为 nodenot node

这依赖于一些较新的 JavaScript 特性。globalThis在 ECMAScript 2020规范中完成。从版本8开始,V8引擎支持可选的链接/无效合并(globalThis.process?.release?.name?部分)。X + (Chrome 80和 Node 14发布于2020年4月21日)。

这并不能直接回答您的问题,因为您想特别检查 Node.js,但是它足够有用,可以保证说:

大多数情况下,如果只想区分浏览器和服务器端的 javascript,只需检查文档是否存在就足够了。

if (typeof document !== 'undefined') {} // do stuff


// This one is overkill, but 100% always works:
if (typeof window !== 'undefined' && window && window.window === window) {
if (typeof window.document !== 'undefined' && document.documentElement) {


}
}

非常老的帖子,但我用其他答案的组合解决了这个问题:

var isNode=()=>!("undefined"!=typeof window||"object"!=typeof module||!module.exports||"object"!=typeof process||!process.moduleLoadList);
console.log(isNode()); //=> false

您可以使用 getter 来检测读取 module.exports的时间,这意味着它是 required。

let required = false;


function myFunction() {
// your code whatever
return required;
}


if (typeof module == 'object') {
Object.defineProperty(module, 'exports', {
get: () => {
required = true;
return myFunction;
}
})
}

在 Chrome Version 106.0.5249.119 (Official Build) (x86_64)和 Node v16.15.0上测试我的控制台