34

如何在 express.js 中定义一个全局函数,没有require我可以调用它

4

2 回答 2

93

如何”很简单:

    global.fnName = function(){ return "hi"; }; // Andreas Hultgren's answer

但是你不需要global前缀;关于对象的事情global是......

    fnName = function(){ return "hi"; }; // i.e. don't do: var name = function(){ ... };
    console.log(fnName());         // this prints "hi"
    console.log(global.fnName());  // this also prints "hi" - it was assigned to global.

“没有require”是一个单独的考虑因素:如果您不使用require,则无法保证您的“全局变量”将在您需要它们时被声明。它强制执行依赖项的加载顺序等。

我为什么”和“是否正确”现在是你应该考虑的隐藏问题。在 javascript 中接受全局变量...

... 应该保留给具有系统范围相关性的对象,并且应该命名它们以避免歧义并最大限度地减少命名冲突的风险 - Angus Croll,Javascript 中的命名空间

ieglobal确实是全球性的:它被您拉入应用程序的每个插件或库的每个作者使用,而不仅仅是您。全局变量之间的命名冲突会破坏您的应用程序。这同样适用于 node.js。

全局变量也被认为是代码异味。在下面的详细部分中,您将看到使用全局变量会很快陷入困境,它们应该真正被视为推动您走向依赖注入和/或命名空间和模块的东西。

Node.js 和 express - 全局变量和函数

这是一个很好的规则:如果您将其上传到 Web 服务器,或与其他人共享,请不要使用全局变量。

global在带有 express.js 的 node.js中的小型“星期六下午”应用程序中是允许的,但如果它们被采用到生产中,往往会在以后引起问题。所以:

  • 模块,exports是最佳实践。
  • 还应该使用注入来减少 javascript 文件之间的耦合。但在所有情况下,您通常需要require确保它们在您需要它们时存在:
  • 对于与视图数据相关的任何内容,您应该真正考虑app.locals数据和/或中间件功能。

    // call this as a function with an input object to merge 
    //  the new properties with any existing ones in app.locals
    app.locals.({
      sayHello: function() { return "hi"; }
    });    
    
    // now you can also use this in a template, like a jade template
    =sayHello()
    

如果您为配置设置目的创建全局变量/函数,以下关于命名空间的注释仍然适用,并且出现了一些约定,例如 config.json 文件(仍在使用require)用于全局访问的设置。

全局变量 - 简单案例

在 javascript 中声明一个全局变量很简单,对于一个函数来说,这个过程没有什么不同。只需省略var通常会在声明中强制使用本地范围的关键字:

// app.js
blah = "boo";
sayHello = function(string toWho) { return "hello " + toWho; }
getVersion = function() { return "0.0.0.1"; }

// routes/main.js
console.log(blah);                     // logs: "boo"
console.log(global.blah);              // logs: "boo"
console.log(sayHello("World"));        // logs: "hello World"
console.log(global.sayHello("World")); // logs: "hello World"
console.log(getVersion());             // logs: "0.0.0.1"

但是,如果您的项目中的两个单独的插件使用全局getVersion函数怎么办 - 您如何获得正确的版本号?另外,你如何确保它getVersion在你需要它之​​前就存在,或者根本就存在?

为什么我们需要require

引用nodejitsu docs内置require函数......

... 是包含存在于单独文件中的模块的最简单方法。的基本功能require是它读取一个 javascript 文件,执行该文件,然后继续返回exports对象

所以”,您可能会问,“require只是确保包含来自另一个文件的模块?何必呢?” 比这更好:你可以把整个文件夹变成一个模块,让你的代码更容易组织和测试 test test,它不仅可以识别文件模块的各种扩展名,而且.js还会各种文件夹中查找。当然,它也会缓存

因此,既然require找到了您的模块,它会确保执行其中的代码,并将您创建的对象放入“命名空间”中:

// module file ./myModule.js
exports.blah = "boo";
exports.sayHello = function(string toWho) { return "hello " + toWho; }

// routes/main.js
var demoModuleReference = require('./myModule.js');
console.log(demoModuleReference.blah);           // logs: "boo"
console.log(demoModuleReference.sayHello("World"));   // logs: "hello World"

在该示例中,demoModuleReference是一个如下所示的对象:

{
  blah: "foo",
  sayHello: [Function]
}

为什么是模块而不是全局变量(又名命名空间和“全局是新的私有”)?

现在好像很复杂?全局变量肯定更容易吗?requires确保以下内容:

  • 它确保有序加载依赖项
  • 它通过对象防止变量名冲突globalexports

mankz.com 上的这个应用程序(仅限 chrome 或 firefox)非常吸引人。根据您使用 js 代码的方式,您很可能在全局范围内发生变量名称冲突。名称冲突无处不在。例如,在浏览器中,它们可以来自扩展。node.js 略有不同,但随着时间的推移,兼容插件越来越多地扩展它(例如,您现在可以加载 jquery)。随着版本的继续,框架将被添加,全局名称冲突将变得更有可能。我上次在 chrome 中运行该应用程序显示了 1200 多个全局命名空间变量。

命名空间 - 为什么?

这种全局命名空间污染很早就由 Douglas Crockford 通过 Eric Miraglia 在文章“ A JavaScript Module Pattern ”中公开。总之:

  • js文件之间需要用到的所有对象都是真正全局的
  • 因此,创建一个唯一的命名空间对象
  • 将返回值分配给匿名函数
  • 在该函数中添加私有方法和变量
  • 用模式做一些有用的事情

例子:

ANDYBROWNSONICSUITE.BoomBox.SoundModule = function () {
  var privateField = "can't touch this";
  return {
    play: function() {
      console.log(privateField);
    }
  }
}

为什么这样好?

  • 现在您只global将世界上的命名空间成员增加了一个,但该成员包含任意数量的项目。
  • 您的应用程序与其他命名空间发生冲突的可能性要小得多
  • 这是一种模式,其他框架希望您使用它来正确地与它们交互。在该参考资料中,jQuery 是一个浏览器插件,但您可以将它与 node 以及您的 app 一起使用,因此库交互策略声明就是一个完美的示例。
  • 这是一种模式,如果我们都遵循它,我们的程序就更有可能相处融洽

当您阅读Crockford 参考资料以及我在开头提到的Croll 参考资料(直接分配部分)时,您就会明白为什么它看起来如此复杂,而不仅仅是做:sound.play = function() { ... }- 易于维护,重构命名空间等只是原因之一。

概括

总之:

  • 我可以创建全局变量吗?是的,很简单,var在声明前省略关键字。
  • 我应该创建全局变量吗?您应该使用 node 和 express 隐式支持的模块模式
  • 为什么我要创建全局变量?如果是用于配置,请使用配置命名空间(例如,如何存储 Node.js 部署设置/配置文件?
于 2013-06-15T13:05:40.467 回答
11

你可以:

global.name = function(){};

但是你真的应该避免使用全局变量,即使可以使用它们。

于 2013-05-28T09:47:43.477 回答