如何在 express.js 中定义一个全局函数,没有require
我可以调用它
2 回答
“如何”很简单:
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
确保以下内容:
- 它确保有序加载依赖项
- 它通过对象防止变量名冲突。
global
exports
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 部署设置/配置文件?)
你可以:
global.name = function(){};
但是你真的应该避免使用全局变量,即使可以使用它们。