67

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.

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.

  1. 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}"
    
  2. 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

10 回答 10

100

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

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

var window = doc.defaultView;

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

幸运的是,有一种方法需要全局范围并测试它是什么——如果您使用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()还可以比较或在 node.js 中找到其他一些全局范围变量,但在实践中与全局比较就足够了。this.process.title==="node"

http://jsfiddle.net/p6yedbqk/

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

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

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

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

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

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

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

于 2015-06-27T15:16:29.513 回答
22

由于显然 Node.js 可以同时拥有这两者(w/NW.js?),我个人的做法是检测该node条目是否存在于process.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

于 2015-07-16T14:10:59.627 回答
11

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

浏览器或节点

你可以这样使用

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

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

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

于 2018-01-31T07:37:46.450 回答
8

您可以根据情况附加到变量窗口或全局。虽然它不是制作多平台 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';
}

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

于 2013-07-10T16:24:41.213 回答
5

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

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

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

祝你好运。

于 2019-09-03T15:01:07.423 回答
2

无论范围如何,这似乎都可以正常工作,除非您命名了其他名称window

const isBrowser = () => typeof window !== `undefined`

if (isBrowser())
  console.log(`is browser`)
else
  console.log(`is node.js`)
于 2021-02-07T06:55:34.267 回答
1
/*
    detect global/window/self in browser, deno, nodejs
    including where 'this' is undefined
*/

const self = new Function('return this')(); // be careful, Function is like eval, use with caution
console.log( 
    (self.window && "window" || self.global && 'global'),
    self.toString().slice('[object '.length, -1).toLowerCase()
);

/*
    browser: window window
    nodejs: global global
    deno: window object
*/
于 2020-03-01T17:42:09.553 回答
1

老问题有很多复杂的答案,甚至是一个 npm 包,但是这个解决方案非常简单和健壮,除非故意破坏(没有解决方案是 100% 精确的顺便说一句,因为你可以在两个环境中设置全局变量)

if (typeof process === 'object' && String(process) === '[object process]') {
  // is Node
} else {
  // is Browser
}

通常(几乎总是)在浏览器上运行的脚本没有全局process对象,即使您偶然创建了一个process = {},它也会在第二种情况下失败。

于 2021-12-15T10:31:22.947 回答
0

来自 pdf.js 的简单条件

第二条件变体过程。构造函数名称 === '进程'

src/shared/ is_node.js

/* Copyright 2018 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* globals process */

// NW.js / Electron is a browser context, but copies some Node.js objects; see
// http://docs.nwjs.io/en/latest/For%20Users/Advanced/JavaScript%20Contexts%20in%20NW.js/#access-nodejs-and-nwjs-api-in-browser-context
// https://www.electronjs.org/docs/api/process#processversionselectron-readonly
// https://www.electronjs.org/docs/api/process#processtype-readonly
const isNodeJS =
  typeof process === "object" &&
  process + "" === "[object process]" &&
  !process.versions.nw &&
  !(process.versions.electron && process.type && process.type !== "browser");

export { isNodeJS }; 
于 2020-10-21T05:11:06.147 回答
0

我并不完全熟悉 Node 环境及其所有情况,例如使用 Babel 或 WebPack 的情况。但是,如果您的代码在浏览器和 Node 控制台中运行,这是一种方法:

if (this.window) {
  // inside browser

} else {
  // inside Node

}
于 2019-12-14T02:45:52.570 回答