环境检测:node.js 或浏览器

IT技术 javascript node.js coffeescript browser-detection
2021-03-03 19:05:14

我正在开发一个 JS 应用程序,它需要在客户端和服务器端工作(在浏览器和 Node.js 中使用 Javascript),我希望能够重用代码的一部分用于双方。

我发现这window是一个只能在浏览器和global节点中访问的变量,所以我可以检测代码在哪个环境中执行(假设没有脚本声明window变量)

他们是两个问题。

  1. 我应该如何检测代码在哪个浏览器中运行。例如,此代码是否正常。(这段代码是内联的,意味着它被一些全局代码包围,在两种环境中重复使用)

    if window?
        totalPath= "../examples/#{path}"
    else
        totalPath= "../../examples/#{path}"
    
  2. 如何在两种环境中使用全局变量?现在,我正在执行以下操作,但这确实感觉不对。

    if window?
        window.DocUtils = {}
        window.docX = []
        window.docXData= []
    else
        global.DocUtils= {}
        global.docX = []
        global.docXData = []
    
6个回答

注意:这个问题有两部分,但因为标题是“环境检测:node.js 或浏览器” - 我会先进入这一部分,因为我想很多人都来这里寻找答案。一个单独的问题可能是有序的。

在 JavaScript 中变量可以通过内部作用域重新定义,因此假设环境没有创建名为 process、global 或 window 的变量很容易失败,例如如果使用 node.js jsdom module,API 用法示例有

var window = doc.defaultView;

之后,根据window变量的存在检测环境将系统地失败,任何在该范围内运行的module都会失败。使用相同的逻辑,任何基于浏览器的代码都可以轻松覆盖globalprocess,因为它们不是该环境中的保留变量。

幸运的是,有一种方法可以要求全局范围并测试它是什么——如果您使用new Function()构造函数创建一个新函数,则 的执行范围将this绑定到全局范围,您可以直接将全局范围与预期值进行比较。*)

因此,要创建一个函数检查全局范围是否为“窗口”将是

var isBrowser=new Function("try {return this===window;}catch(e){ return false;}");

// tests if global scope is bound to window
if(isBrowser()) console.log("running under browser");

并且测试全局范围是否绑定到“全局”的函数将是

var isNode=new Function("try {return this===global;}catch(e){return false;}");

// tests if global scope is bound to "global"
if(isNode()) console.log("running under node.js");

try... catch -part 将确保如果未定义变量,false则返回。

如果isNode()愿意,也可以比较this.process.title==="node"或在 node.js 中找到的其他一些全局范围变量,但在实践中与全局比较应该足够了。

http://jsfiddle.net/p6yedbqk/

注意:不建议检测运行环境。但是,它在特定环境中可能很有用,例如开发和测试环境具有一些全局范围的已知特征。

现在 - 答案的第二部分。环境检测完成后,您可以选择要使用的基于环境的策略(如果有)将“全局”变量绑定到您的应用程序。

在我看来,这里推荐的策略是使用单例模式将您的设置绑定到一个类中。SO中已经有一个很好的替代方案列表

在 JavaScript 中实现单例的最简单/最干净的方法

因此,如果您不需要“全局”变量,并且根本不需要环境检测,则可能会发现,只需使用单例模式定义一个module,该module将为您存储值。好吧,有人可能会争辩说module本身是一个全局变量,在 JavaScript 中它实际上是一个全局变量,但至少在理论上它看起来更简洁一些。

*) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function

注意:使用 Function 构造函数创建的函数不会为其创建上下文创建闭包;它们总是在全局范围内创建。运行它们时,它们只能访问自己的局部变量和全局变量,而不能访问调用 Function 构造函数的范围内的变量。

无一例外: (function (){ return (typeof window !== 'undefined') && (this === window); }).call(undefined);
2021-04-22 19:05:14
嗨,感谢您提供这个解决方案,我尝试了很多东西,但这个方法很完美,所以我使用它发布了一个 npm 包,我希望你不介意……查看npmjs.com/package/detect-节点
2021-04-27 19:05:14
@TeroTolonen,你为什么说不建议检测环境?许多主要框架都可以在节点和 Web 环境中工作。包含“获取文件”功能的module在每个环境中的工作方式都大不相同,例如用于 Web 的 XMLHttpRequest、用于 Node 的 http 或路径module。
2021-05-06 19:05:14
是的,我同意,但是如果您想使用它 100 次,那么阅读和理解一个简单的函数就变得容易了。
2021-05-11 19:05:14
@abhirathore2006 非常简约 ;) 仍然可以是单行的,module.exports = (new Function("try {return this===global;}catch(e){return false;}")))();
2021-05-19 19:05:14

由于显然 Node.js 可以同时拥有两者(w/ NW.js?),我个人的做法是检测node条目是否存在于object.jsprocess.versions

var isNode = false;    
if (typeof process === 'object') {
  if (typeof process.versions === 'object') {
    if (typeof process.versions.node !== 'undefined') {
      isNode = true;
    }
  }
}

多级条件是为了避免由于某些浏览器的限制而在搜索未定义变量时出错。

参考:https : //nodejs.org/api/process.html#process_process_versions

在现代语法中: const isNode = typeof process !== "undefined" && process?.versions?.node;
2021-05-07 19:05:14
@brillout 关闭: function isNodejs() { return typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node !== 'undefined'; }
2021-05-08 19:05:14
process.versions.node !== 'undefined' 应该 process.versions.node !== undefined
2021-05-08 19:05:14
@avalanche1 请注意,它正在使用,typeof所以很好。
2021-05-11 19:05:14
单线: function isNodejs() { return typeof "process" !== "undefined" && process && process.versions && process.versions.node; }
2021-05-16 19:05:14

有一个专门用于此的 npm 包,它可以在客户端和服务器端使用。

浏览器或节点

你可以这样使用

if (isBrowser) {
  // do browser only stuff
}

if (isNode) {
  // do node.js only stuff
}

免责声明:我是这个包的作者:)

您可以根据情况附加到可变窗口或全局。虽然它不是制作多平台 JS 应用程序的推荐方式:

var app = window ? window : global;

拥有全局变量会更好,它将用于应用程序的逻辑,但将由基于不同平台的部分组成。就像是:

var app = {
    env: '',
    agent: ''
};

if (window) {
    app.env = 'browser';
    app.agent = navigator.userAgent;
} else if (process) {
    app.env = 'node';
}

所以这个想法是你的主要应用程序逻辑将完全相同并且将使用相同的对象,只有该全局对象必须根据环境进行更改。这使您的应用程序在平台方面更加便携和灵活。

谢谢,实际上我找到了另一篇处理咖啡脚本部分的帖子stackoverflow.com/questions/4214731/...,所以这很棒
2021-05-01 19:05:14
请注意,var app = window ? window : global;可以简化为var app = window || global;.
2021-05-07 19:05:14
var app = (typeof(window) != 'undefined') ? window : global 似乎工作
2021-05-11 19:05:14
在节点var app = window ? window : global;产生Thrown: ReferenceError: window is not defined. 你有没有测试它在节点上工作?(使用的版本v11.15.0
2021-05-15 19:05:14

我知道这是一个(1.5 年)老问题的迟到答案,但为什么不复制jQuery的源代码?

if (typeof module === "object" && typeof module.exports === "object") {
  // node
}

if (typeof window !== "undefined" && typeof window.document !== "undefined") {
  // browser
}

祝你好运。

我想知道什么时候window定义但window.document不是
2021-05-01 19:05:14
@nonopolarity 如果你不检查window.document并且你的nodejswindow定义了一个变量,你会得到浏览器的误报。
2021-05-02 19:05:14