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

2020/10/28 18:43 · javascript ·  · 0评论

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

例如,我只想exports.x = y;在Node.js下运行。如何执行此测试?


发布此问题时,我不知道Node.js模块功能是否基于CommonJS

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

脚本如何判断是否已将其作为CommonJS模块使用?

通过寻找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模式。

Well there's no reliable way to detect running in Node.js since every website could easily declare the same variables, yet, since there's no window object in Node.js by default you can go the other way around and check whether you're running inside a Browser.

This is what I use for libs that should work both in a Browser and under Node.js:

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

} else {
    window.foo = {};
}

It might still explode in case that window is defined in Node.js but there's no good reason for someone do this, since you would explicitly need to leave out var or set the property on the global object.

EDIT

要检测是否需要脚本作为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在那里声明。

我目前偶然发现了一个错误的节点检测,由于误导特征检测,它知道电子中的节点环境以下解决方案可明确识别过程环境。


仅识别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(带有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开始工作我没有找到先前版本的唯一标识符。


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

The problem with trying to figure out what environment your code is running in is that any object can be modified and declared making it close to impossible to figure out which objects are native to the environment, and which have been modified by the program.

However, there are a few tricks we can use to figure out for sure what environment you are in.

Lets start out with the generally accepted solution that's used in the underscore library:

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

This technique is actually perfectly fine for the server side, as when the require function is called, it resets the this object to an empty object, and redefines module for you again, meaning you don't have to worry about any outside tampering. As long as your code is loaded in with require, you are safe.

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

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

this.module !== module

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

因此,最终测试是:

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

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

以下内容在浏览器中有效,除非有意,故意破坏:

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

am

这也是一种很酷的方法:

const isBrowser = this.window === this;

之所以可行,是因为在浏览器中,全局“ this”变量具有一个称为“ window”的自引用。该自引用在Node中不存在。

  • 在浏览器中,“ this”是对称为“ window”的全局对象的引用。
  • 在节点中,“ this”是对module.exports对象的引用。

    • “ this”不是对称为“ global”的Node全局对象的引用。
    • “ this”不是对模块变量声明空间的引用。

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

this.window = this;

在执行检查之前。

另一个环境检测

(含义:这里的大多数答案都可以。)

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在全局范围内声明一个全局变量时,就会发生这种情况或某些邪恶的开发人员以某种方式将代码注入到我们的环境中。

我们可能会在捕获到此代码后阻止代码执行,但是我们的应用程序的许多其他依赖关系可能会被捕获。因此,最终代码将中断。如果您的代码足够好,那么您就不必关心别人可能犯的每一个愚蠢的错误。

所以呢?

如果定位两个环境:浏览器和节点;
"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;
}

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

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

怎么样使用过程中的对象和检查execPathnode

process.execPath

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

例:

/ usr / local / bin / node

脚本如何判断是否已将其作为commonjs模块使用?

相关:要检查它是否需要作为模块还是直接在节点中运行,可以检查require.main !== module
http://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)。在我看来,除非您在浏览器中或希望您相信自己在浏览器中的环境中,否则这种情况不太可能发生。在所有其他情况下,如果声明了全局“模块”变量,则使用该变量,否则使用全局对象。

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

记录在这里

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

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

runtime值将为nodenot node

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

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

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

这是一种确保服务器端和客户端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是一个对象。:-)


原始答案:

我确定可以检查一些特定于NodeJS的符号(EventEmitter也许 不是,您必须使用它require来获取事件模块;请参见下文),但是正如David所说,最好还是检测一下该功能(而是而不是环境)。

更新:也许类似:

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

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

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

正如您要专门检查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) {

   }
}

获取node.js的源代码并对其进行更改,以定义类似的变量runningOnNodeJS在代码中检查该变量。

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

很老的帖子,但是我只是通过尝试包装require语句解决了-catch

try {
     var fs = require('fs')
} catch(e) {
     alert('you are not in node !!!')
}
本文地址:http://javascript.askforanswer.com/ruhejianchajiaobenshifouzainode-jsxiayunxing.html
文章标签: ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!