Node模块的module.exports返回undefined。

5

我遇到了一个关于Node.js和module.exports的问题。我知道module.exports是一个返回对象的调用,该对象具有分配给它的任何属性。

假设我的文件结构如下:

// formatting.js

function Format(text) {
    this.text = text;
}

module.exports = Format;

使用以下内容:

// index.js

var formatting = require('./formatting');

有没有一种初始化Format对象并像这样使用它的方法?
formatting('foo');
console.log(formatting.text);

每当我尝试那样做时,就会出现错误提示:formatting is not a function。我随后不得不这样做:
var x = new formatting('foo');
console.log(x.text);

这似乎有点繁琐。

在像 keypressrequest 这样的模块中,它们可以直接使用,像这样:

var keypress = require('keypress');

keypress(std.in);

或者

var request = require('request);

request('http://www.google.com', function (error, response, body) {
  if (!error && response.statusCode == 200) {
    console.log(body) // Show the HTML for the Google homepage.
  }
})

这是如何运作的呢?

这个能用吗? var formatting = require('./formatting.js'); - Will
在你的代码中,你正在从格式化模块返回函数构造器。相反,你想将它作为一个对象返回,这样你就可以直接使用它。 - Pardeep Dhingra
@PardeepDhingra 我该怎么做? - apizzimenti
@apizzimenti 请检查我的答案。 - lleaff
3个回答

5
我建议将new调用封装在一个函数中,然后返回该函数:
function Format(text) {
  this.text = text;
}

function formatting(text) {
  return new Format(text);
}

module.exports = formatting;

这样你仍然应该能够做到:

var format = formatting('foo');
console.log(format.text);


编辑:

request部分,需要记住的一件事是,在JavaScript中,函数仍然是对象。这意味着您仍然可以向它们添加属性和方法。这就是他们在request中所做的,尽管总体上有点太复杂,无法在本答案中解释每个细节。从我所了解的情况来看,他们向request函数添加了一堆方法(对象上的函数)。这就是为什么您可以立即调用那些方法,如request(blah, blah).pipe(blah).on(blah)。根据从调用request函数返回的内容,您可以在其后面链接一些其他方法。当您使用request时,它不是一个对象,而是一个函数(但从技术上讲仍然是一个对象)。为了证明函数仍然是对象,并且可以向它们添加方法,可以查看以下简单示例代码:

function hey(){
  return;
}

hey.sayHello = function(name) {
  console.log('Hello ' + name);
} 

hey.sayHello('John'); //=> Hello John

这基本上就是他们正在做的事情,只不过更加复杂,有更多的东西在进行。

1
什么意思?当您初始化它时,难道不想将返回的对象设置为一个变量吗?是的,我想这样做可能是有可能的,例如 console.log(formatting('foo').text),但那样做有什么意义呢? - Sam
好的,我明白了。在你的第二个代码块中,formatting 是指向 require('./formatting') 的引用,对吗? - apizzimenti
1
是的,如果在所有其他的requires的顶部你写了var formatting = require('./formatting');,那么我就从那里得到了formatting()函数。 - Sam
1
另外,如果你对request的工作原理感到好奇,你可以查看使其运行的代码:https://github.com/request/request/blob/master/index.js - Sam
看看我的新编辑。我相信还有很多人可以详细解释,但希望这能帮助你更好地理解request的工作原理。我不会尝试进一步解释它,因为我已经超出了自己的能力范围。 - Sam
显示剩余2条评论

3
module.exports = Format;

当您使用 require('./formatting') 时,将返回 Format 构造函数。

另一方面,下面的代码将返回 Format 的实例,您可以直接调用其方法:

module.exports = new Format();

如果我这样做,然后执行formatting('text'),并尝试在控制台中打印对象的text属性,它仍然会显示“formatting不是函数”。我做错了什么吗? - apizzimenti
是的,因为一旦你使用new从构造函数创建一个对象,它就不再是一个函数,而是通过将构造函数应用于新对象创建的对象。因此,如果您想要一个既是对象又是函数的模块(例如jQuery),则构造函数模式不是正确的方法。相反,首先导出一个函数,然后附加属性到它上面。 - lleaff
@lleaff 如果我选择第二个选项,然后在 index.js 中运行 format('foo'),那么它会将 'foo' 分配给 text 属性吗?module.exports 返回类的空实例,然后使用 format('foo') 调用其构造函数函数正确地分配属性? - apizzimenti
2
请查看Sam的答案,我认为这就是你想要做的。 - lleaff

1
尝试一下这个:

module formatting:

function Format() {
    this.setter = function(text) {
      this.text = text;
    }
    this.show = function() {
      console.log(this.text);
    } 
}
//this says I want to return empty object of Format type created by Format constructor.

module.exports = new Format();

index.js

var formatting = require('./formatting');
formatting('Welcome');
console.log(formatting.show());

你能详细说明一下 module.exports = new Format(); 吗? - BlackMamba
这个对你有用吗?它仍然在调用formatting('Welcome')的那一行返回错误formatting不是一个函数 - apizzimenti
1
由于格式化是格式类的对象,因此我们无法将“文本”传递给对象,我们需要一个setter方法。 - Pardeep Dhingra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接