环境检测:node.js 或浏览器
我正在开发一个 JS 应用程序,它需要在客户端和服务器端(在浏览器上的 Javascript 和 Node.js 中)都可以工作,我希望能够重用用于双方的代码.
I'm developping a JS-app that needs to work both on the client side and the server side (in Javascript on a browser and in Node.js), and I would like to be able to reuse the parts of the code that are used for both sides.
我发现 window
是一个只能在浏览器上访问的变量,而 global
在节点中,所以我可以检测代码在哪个环境中执行(假设没有脚本声明 window
变量)
I have found out that window
was a variable only accessible on Browsers, and global
in node, so I can detect in which environment the code is executing (assuming that no script declares the window
variable)
这是两个问题.
我应该如何检测代码在哪个浏览器中运行.例如,此代码是否正常.(这段代码是内联的,意味着它被一些全局代码包围,在两种环境中都可以重用)
How should I detect in which browser the code is running. For example, is this code OK. (This code is inline, meaning that it is surrounded by some global code, reused for both environments)
if window?
totalPath= "../examples/#{path}"
else
totalPath= "../../examples/#{path}"
如何在两个环境中使用全局变量?现在,我正在执行以下操作,但这确实感觉不对.
How can I use global variables for both environments ? Now, I'm doing the following, but this really doesn't feel right.
if window?
window.DocUtils = {}
window.docX = []
window.docXData= []
else
global.DocUtils= {}
global.docX = []
global.docXData = []
推荐答案
注意:这个问题有两个部分,但是因为标题是环境检测:node.js 或浏览器";- 我将首先进入这部分,因为我想很多人都来这里寻找答案.可能需要一个单独的问题.
在 JavaScript 中,变量可以由内部作用域重新定义,因此假设环境没有创建名为 process、global 或 window 的变量很容易失败,例如如果使用 node.js jsdom 模块,API使用示例有
In JavaScript variables can be redefined by the inner scopes, thus assuming that environment has not created variables named as process, global or window could easily fail, for example if one is using node.js jsdom module, the API usage example has
var window = doc.defaultView;
在此之后,基于 window
变量的存在检测环境将系统性地因在该范围内运行的任何模块而失败.使用相同的逻辑,任何基于浏览器的代码都可以轻松覆盖 global
或 process
,因为它们不是该环境中的保留变量.
After which detecting the environment based on the existence of window
variable would systematically fail by any module running under that scope. With the same logic any browser based code could easily overwrite global
or process
, because they are not reserved variables in that environment.
幸运的是,有一种方法需要全局范围并测试它是什么——如果你使用 new Function()
构造函数创建一个新函数,this的执行范围code> 绑定到全局范围,您可以将全局范围直接与预期值进行比较.*)
Fortunately there is a way of requiring the global scope and testing what it is - if you create a new function using a new Function()
constructor, the execution scope of this
is bound to the global scope and you can compare the global scope directly to the expected value. *)
所以要创建一个函数,检查全局范围是否为窗口";会是
So to create a function check if the global scope is "window" would be
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");
以及测试全局范围是否绑定到全局"的函数.会是
And function to test if global scope is bound to "global" would be
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
.
the try... catch -part will makes sure that if variable is not defined, false
is returned.
isNode()
还可以比较 this.process.title==="node"
或其他在 node.js 中找到的全局范围变量,如果你愿意的话,但在实践中与全局比较就足够了.
The isNode()
could also compare this.process.title==="node"
or some other global scope variable found inside node.js if you will, but comparing to the global should be enough in practice.
http://jsfiddle.net/p6yedbqk/
注意:不建议检测运行环境.但是,它在特定环境中可能很有用,例如在全球范围内具有一些已知特征的开发和测试环境.
NOTE: detecting the running environment is not recommended. However, it can be useful in a specific environment, like development and testing environment which has some known characteristics for the global scope.
现在 - 答案的第二部分. 环境检测完成后,您可以选择要使用哪个基于环境的策略(如果有)来绑定您的变量全球"到您的应用程序.
Now - the second part of the answer. after the environment detection has been done, you can select which environment based strategy you want to use (if any) to bind your variable which are "global" to your application.
在我看来,这里推荐的策略是使用单例模式将您的设置绑定到一个类中.SO中已经有一个很好的替代品列表
The recommended strategy here, in my opinion, would be to use a singleton pattern to bind your settings inside a class. There is a good list of alternatives already in SO
在 JavaScript 中实现单例的最简单/最干净的方法
因此,如果您不需要全局";变量,您根本不需要环境检测,只需使用单例模式定义一个模块,该模块将为您存储值.好的,可以说模块本身是一个全局变量,在 JavaScript 中它实际上是一个全局变量,但至少在理论上它看起来更简洁一些.
So, it may turn out if you do not need a "global" variable, and you do not need the environment detection at all, just use the singleton pattern to defined a module, which will store the values for you. OK, one can argue that the module itself is a global variable, which in JavaScript it actually is, but at least in theory it looks a bit cleaner way of doing it.
*) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
注意:使用 Function 构造函数创建的函数不会创建关闭其创建上下文;它们总是在全球范围.运行它们时,它们将只能访问他们自己的局部变量和全局变量,而不是范围内的变量在其中调用了 Function 构造函数.
Note: Functions created with the Function constructor do not create closures to their creation contexts; they always are created in the global scope. When running them, they will only be able to access their own local variables and global ones, not the ones from the scope in which the Function constructor was called.
相关文章