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

时间:2010-11-19 11:41:39

标签: javascript node.js commonjs

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

所以,例如,我想这样做:

exports.x = y;

仅当它在node.js下运行时我该如何进行这项测试?

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

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

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

23 个答案:

答案 0 :(得分:87)

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

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

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

} else {
    window.foo = {};
}

如果在Node.js中定义了window但是有人没有的原因,它可能仍然会爆炸,因为你明确需要省略{{1}或者在var对象上设置属性。

修改

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

exports

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

尝试通过一些疯狂的function (exports, require, module, __filename, __dirname) { 内容来检测,而只是使用上面检查浏览器的示例代码。 Node.js是一种更清洁的环境,所以arguments.callee.toString()不太可能在那里声明。

答案 1 :(得分: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._ = _;
    }
})();

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

答案 2 :(得分:29)

由于误导性的功能检测,我目前偶然发现错误的Node检测结果 意识到Electron中的节点环境。以下解决方案明确标识了流程环境。

仅识别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)

本声明使用Node 5.5.0,Electron 0.36.9(带节点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。我没有为以前的版本找到唯一的标识符。

P.s。:我在这里发布了答案,因为问题引导我,尽管OP正在寻找不同问题的答案。

答案 3 :(得分:25)

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

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

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

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

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

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

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

this.module !== module

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

因此,最终的测试是:

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

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

答案 4 :(得分:20)

以下内容适用于浏览器,除非故意明确破坏:

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

的Bam

答案 5 :(得分:13)

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

const isBrowser = this.window === this;

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

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

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

this.window = this;

在执行检查之前。

答案 6 :(得分: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 ?..
}

有点偏执吗?您可以通过检查更多globals来使其更加详细。

但是DON' T!。

以上所有这些都可以伪造/模拟。

例如伪造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个环境:浏览器和节点;
"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块中执行同步特征检测。 (这些将花费几毫秒来执行)。

e.g。

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

答案 7 :(得分:9)

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

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

答案 8 :(得分:4)

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

记录here

答案 9 :(得分:4)

以下是我对上述内容的修改:

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

答案 10 :(得分:4)

  

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

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

答案 11 :(得分:4)

如何使用流程对象并检查node的{​​{3}}?

  

process.execPath

     

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

     
    

示例:

         

的/ usr / local / bin中/节点

  

答案 12 :(得分:3)

在撰写本文时,此答案更多地是“即将推出”选项,因为它利用了JavaScript的新功能。

const runtime = globalThis.process?.release?.name || 'not node'
console.log(runtime)

runtime的值为nodenot node

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

这种方法带有一定的电流限制。然而;浏览器/节点的发布速度,最终将是可靠的一线。

答案 13 :(得分:2)

这是确保服务器端和客户端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;
}())

答案 14 :(得分:2)

Node.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");
}

答案 15 :(得分:1)

修改:关于您更新的问题:“脚本如何判断是否需要将其作为commonjs模块?”我认为不可以。您可以检查exports是否为对象(if (typeof exports === "object")),因为the spec要求将其提供给模块,但所有这些都告诉您...... 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。 : - )

答案 16 :(得分:0)

很老的帖子,但我结合其他答案解决了:

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

答案 17 :(得分:0)

这并不能直接回答您的问题,因为您想专门检查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) {

   }
}

答案 18 :(得分:0)

来自调试包的来源:

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

https://github.com/visionmedia/debug/blob/master/src/index.js#L6

答案 19 :(得分:0)

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

答案 20 :(得分:0)

我认为这很简单。

     if (process && process.title === 'node') {
        // Your code here
     }

答案 21 :(得分:-1)

非常老的帖子,但我刚刚通过在try-catch

中包装require语句来解决它
    try {

           var fs = require('fs')

} catch(e) {
       alert('you are not in node !!!')
}

答案 22 :(得分:-1)

获取node.js的来源并更改它以定义类似runningOnNodeJS的变量。检查代码中的变量。

如果您不能拥有自己的node.js私有版本,请在项目中打开功能请求。要求他们定义一个变量,它为您提供正在运行的node.js的版本。然后检查该变量。