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.
我正在开发一个js应用程序,它需要在客户端和服务器端(在浏览器的Javascript和Node.js中)同时工作,我希望能够重用用于双方的代码部分。
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)
我发现窗口是一个只能在浏览器*问的变量,并且在节点上是全局的,所以我可以检测代码执行的环境(假设没有脚本声明窗口变量)
They are two problems.
他们是两个问题。
-
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 = []
4 个解决方案
#1
54
NOTE: This question had two parts, but because the title was "Environment detection: node.js or browser" - I will get to this part first, because I guess many people are coming here to look for an answer to that. A separate question might be in order.
注意:这个问题有两个部分,但是因为标题是“环境检测:节点”。我将首先介绍这一部分,因为我想很多人会来这里寻找答案。另一个问题可能是恰当的。
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
在JavaScript中,内部作用域可以重新定义变量,因此假设环境没有创建名为process的变量,全局或窗口很容易失败,例如,如果使用节点。js jsdom模块,API使用例有。
var window = doc.defaultView;
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.
在此之后,基于窗口变量的存在检测环境将被在该范围内运行的任何模块系统地失败。使用相同的逻辑,任何基于浏览器的代码都可以轻松覆盖全局或进程,因为它们不是该环境中的保留变量。
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 binded 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 binded to window
if(isBrowser()) console.log("running under browser");
And function to test if global sope is binded to "global" would be
如果全球sope被绑定到“全球”,那么它的功能将会是。
var isNode=new Function("try {return this===global;}catch(e){return false;}");
// tests if global scope is binded to "global"
if(isNode()) console.log("running under node.js");
the try... catch -part will makes sure that if variable is not defined, false
is returned.
试一试……catch -part将确保如果未定义变量,则返回false。
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.
isNode()也可以比较这个.process。title=== ="node"或在node内部找到的其他全局范围变量。如果你愿意,可以用js,但在实践中与全局比较就足够了。
http://jsfiddle.net/p6yedbqk/
NOTE: detecting the running environment is not recommended. However, it can be useful in a specifiic 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.
现在,答案的第二部分。在环境检测完成之后,您可以选择要使用的基于环境的策略(如果有的话)将“全局”变量绑定到应用程序。
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
在我看来,这里推荐的策略是使用单例模式来绑定类中的设置。因此,已经有了一份很好的替代方案清单
Simplest/Cleanest way to implement singleton in 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.
因此,如果您不需要一个“全局变量”,并且您不需要环境检测,只需使用singleton模式来定义一个模块,它将为您存储这些值。好吧,我们可以说模块本身是一个全局变量,在JavaScript中它实际上是,但至少在理论上它看起来更简洁。
*) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
*)https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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.
注意:使用函数构造函数创建的函数不会为其创建上下文创建闭包;它们总是在全局范围内创建的。在运行它们时,它们只能访问自己的本地变量和全局变量,而不能访问调用函数构造函数的范围内的变量。
#2
10
Since apparently Node.js could have both (w/ NW.js?), my personnal way to do it is by detecting if the node
entry exists in process.versions
object.
因为显然节点。js可能都有(w/ NW.js?),我个人的做法是检测进程中是否存在节点条目。版本对象。
var isNode = false;
if (typeof process === 'object') {
if (typeof process.versions === 'object') {
if (typeof process.versions.node !== 'undefined') {
isNode = true;
}
}
}
The multilevel of conditions is to avoid errors while searching into an undefined variable due to some browsers limitations.
由于某些浏览器的限制,在搜索未定义的变量时,需要避免错误。
Reference: https://nodejs.org/api/process.html#process_process_versions
参考:https://nodejs.org/api/process.html # process_process_versions
#3
8
You can attach to variable window or global - based on situation. Though it is not a recommended way of making multi-platform JS application:
您可以根据情况附加到可变窗口或全局窗口。虽然这并不是一种构建多平台JS应用的推荐方式:
var app = window ? window : global;
It is much better to have your global variable, that will be used over logic of application, but will be made of parts of based on different platforms. Something like:
使用全局变量比使用应用程序的逻辑要好得多,但是要根据不同的平台组成部分。喜欢的东西:
var app = {
env: '',
agent: ''
};
if (window) {
app.env = 'browser';
app.agent = navigator.userAgent;
} else if (process) {
app.env = 'node';
}
So the idea is that your main application logic will be absolutely the same and will use same object, only that global object have to be changed based on environment. That makes your application much more portable and flexible in terms of platforms.
其思想是,你的主应用程序逻辑将是完全相同的,并且将使用相同的对象,只有全局对象必须根据环境进行修改。这使得您的应用程序在平台方面更具可移植性和灵活性。
#4
2
There is an npm package just for this and it can be used both on client-side and server-side.
这里有一个npm包,可以在客户端和服务器端使用。
browser-or-node
You can use it this way
你可以用这种方法
if (isBrowser) {
// do browser only stuff
}
if (isNode) {
// do node.js only stuff
}
Disclaimer: I am the author of this package :)
免责声明:我是这个包裹的作者:)
#1
54
NOTE: This question had two parts, but because the title was "Environment detection: node.js or browser" - I will get to this part first, because I guess many people are coming here to look for an answer to that. A separate question might be in order.
注意:这个问题有两个部分,但是因为标题是“环境检测:节点”。我将首先介绍这一部分,因为我想很多人会来这里寻找答案。另一个问题可能是恰当的。
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
在JavaScript中,内部作用域可以重新定义变量,因此假设环境没有创建名为process的变量,全局或窗口很容易失败,例如,如果使用节点。js jsdom模块,API使用例有。
var window = doc.defaultView;
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.
在此之后,基于窗口变量的存在检测环境将被在该范围内运行的任何模块系统地失败。使用相同的逻辑,任何基于浏览器的代码都可以轻松覆盖全局或进程,因为它们不是该环境中的保留变量。
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 binded 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 binded to window
if(isBrowser()) console.log("running under browser");
And function to test if global sope is binded to "global" would be
如果全球sope被绑定到“全球”,那么它的功能将会是。
var isNode=new Function("try {return this===global;}catch(e){return false;}");
// tests if global scope is binded to "global"
if(isNode()) console.log("running under node.js");
the try... catch -part will makes sure that if variable is not defined, false
is returned.
试一试……catch -part将确保如果未定义变量,则返回false。
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.
isNode()也可以比较这个.process。title=== ="node"或在node内部找到的其他全局范围变量。如果你愿意,可以用js,但在实践中与全局比较就足够了。
http://jsfiddle.net/p6yedbqk/
NOTE: detecting the running environment is not recommended. However, it can be useful in a specifiic 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.
现在,答案的第二部分。在环境检测完成之后,您可以选择要使用的基于环境的策略(如果有的话)将“全局”变量绑定到应用程序。
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
在我看来,这里推荐的策略是使用单例模式来绑定类中的设置。因此,已经有了一份很好的替代方案清单
Simplest/Cleanest way to implement singleton in 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.
因此,如果您不需要一个“全局变量”,并且您不需要环境检测,只需使用singleton模式来定义一个模块,它将为您存储这些值。好吧,我们可以说模块本身是一个全局变量,在JavaScript中它实际上是,但至少在理论上它看起来更简洁。
*) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
*)https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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.
注意:使用函数构造函数创建的函数不会为其创建上下文创建闭包;它们总是在全局范围内创建的。在运行它们时,它们只能访问自己的本地变量和全局变量,而不能访问调用函数构造函数的范围内的变量。
#2
10
Since apparently Node.js could have both (w/ NW.js?), my personnal way to do it is by detecting if the node
entry exists in process.versions
object.
因为显然节点。js可能都有(w/ NW.js?),我个人的做法是检测进程中是否存在节点条目。版本对象。
var isNode = false;
if (typeof process === 'object') {
if (typeof process.versions === 'object') {
if (typeof process.versions.node !== 'undefined') {
isNode = true;
}
}
}
The multilevel of conditions is to avoid errors while searching into an undefined variable due to some browsers limitations.
由于某些浏览器的限制,在搜索未定义的变量时,需要避免错误。
Reference: https://nodejs.org/api/process.html#process_process_versions
参考:https://nodejs.org/api/process.html # process_process_versions
#3
8
You can attach to variable window or global - based on situation. Though it is not a recommended way of making multi-platform JS application:
您可以根据情况附加到可变窗口或全局窗口。虽然这并不是一种构建多平台JS应用的推荐方式:
var app = window ? window : global;
It is much better to have your global variable, that will be used over logic of application, but will be made of parts of based on different platforms. Something like:
使用全局变量比使用应用程序的逻辑要好得多,但是要根据不同的平台组成部分。喜欢的东西:
var app = {
env: '',
agent: ''
};
if (window) {
app.env = 'browser';
app.agent = navigator.userAgent;
} else if (process) {
app.env = 'node';
}
So the idea is that your main application logic will be absolutely the same and will use same object, only that global object have to be changed based on environment. That makes your application much more portable and flexible in terms of platforms.
其思想是,你的主应用程序逻辑将是完全相同的,并且将使用相同的对象,只有全局对象必须根据环境进行修改。这使得您的应用程序在平台方面更具可移植性和灵活性。
#4
2
There is an npm package just for this and it can be used both on client-side and server-side.
这里有一个npm包,可以在客户端和服务器端使用。
browser-or-node
You can use it this way
你可以用这种方法
if (isBrowser) {
// do browser only stuff
}
if (isNode) {
// do node.js only stuff
}
Disclaimer: I am the author of this package :)
免责声明:我是这个包裹的作者:)