Node.js - 使用module.exports作为构造函数

126

根据 Node.js 手册:

如果您想要模块的导出根部是一个函数 (例如构造函数), 或者如果您想要在一个赋值操作中导出一个完整的对象而不是逐个属性地构建它,将其分配给 module.exports 而不是 exports。

给出了以下示例:

// file: square.js
module.exports = function(width) {
  return {
    area: function() {
      return width * width;
    }
  };
}

并且可以像这样使用:

var square = require('./square.js');
var mySquare = square(2);
console.log('The area of my square is ' + mySquare.area());

我的问题是:为什么这个例子没有使用 square 作为一个对象?以下内容是否有效,并且能让这个例子更加“面向对象”?
var Square = require('./square.js');
var mySquare = new Square(2);
console.log('The area of my square is ' + mySquare.area());

1
你的例子是语法错误。将 square 重命名为 Square 后,new square() 就不存在了。 - Sukima
3
抱歉,那是打错字了。已经修正了。我的意图是展示以大写字母开头的对象/函数名称和以小写字母开头的实例名称。 - Naresh
4
我已经料到了,这就是我写我的答案的原因。我只是想说我很高兴其他人也像我一样看待模块。我经常使用new关键字并组织我的模块来导出单个构造函数。我发现这可以使解决方案的可读性和概念化更容易。我一眼就能看出我打算使用什么样的结构。赞同你像我一样思考 ;) - Sukima
5个回答

179

CommonJS模块有两种定义导出属性的方式。无论哪种情况,都需要返回一个对象/函数。由于在JavaScript中函数是一等公民,它们可以像对象一样运作(技术上来说它们就是对象)。话虽如此,你关于使用new关键字的问题有一个简单的答案:是的。我会举例说明...

模块导出

您可以使用提供的exports变量将属性附加到其上。在另一个模块中被引用时,这些分配的属性变得可用。或者,您可以将一个对象分配给module.exports属性。无论哪种情况,require()返回的是对module.exports值的引用。

以下是一个伪代码示例,展示了如何定义一个模块:

var theModule = {
  exports: {}
};

(function(module, exports, require) {

  // Your module code goes here

})(theModule, theModule.exports, theRequireFunction);
在上面的示例中,module.exportsexports是同一个对象。有趣的是,在您的CommonJS模块中看不到这些细节,因为整个系统已经为您处理了所有内容,您只需要知道有一个模块对象,其中有一个导出属性和一个导出变量,指向与module.exports相同的东西。

带构造函数的require

由于您可以直接将函数附加到module.exports,因此您基本上可以返回一个函数,并且像任何函数一样,它可以被视为构造函数(这是斜体字,因为在JavaScript中函数和构造函数之间唯一的区别是您的使用意图。从技术上讲,没有区别。)
因此,以下代码是完全可行的,我个人鼓励这样做:
// My module
function MyObject(bar) {
  this.bar = bar;
}

MyObject.prototype.foo = function foo() {
  console.log(this.bar);
};

module.exports = MyObject;

// In another module:
var MyObjectOrSomeCleverName = require("./my_object.js");
var my_obj_instance = new MyObjectOrSomeCleverName("foobar");
my_obj_instance.foo(); // => "foobar"

非构造函数的要求

对于非构造函数,也是同样的道理:

// My Module
exports.someFunction = function someFunction(msg) {
  console.log(msg);
}

// In another module
var MyModule = require("./my_module.js");
MyModule.someFunction("foobar"); // => "foobar"

2
我可以使用 require('./my-object.js')("foobar") 的简写吗?还是 require('module')(params) 语法用于不同的用例? - Hampus Ahlgren
1
没有任何限制,一切都只是JavaScript。因此,您可以使用更短的语法。 - Sukima
3
模块定义的伪代码示例完全澄清了我对Node.js模块系统的理解。谢谢! - Nitax

137

在我看来,一些node.js的例子相当牵强附会。

在现实世界中,您可能会期望看到更像这样的东西。

// square.js
function Square(width) {

  if (!(this instanceof Square)) {
    return new Square(width);
  }

  this.width = width;
};

Square.prototype.area = function area() {
  return Math.pow(this.width, 2);
};

module.exports = Square;

用法

var Square = require("./square");

// you can use `new` keyword
var s = new Square(5);
s.area(); // 25

// or you can skip it!
var s2 = Square(10);
s2.area(); // 100

对于ES6的使用者

class Square {
  constructor(width) {
    this.width = width;
  }
  area() {
    return Math.pow(this.width, 2);
  }
}

export default Square;

在ES6中使用它

import Square from "./square";
// ...
当使用类时,您必须使用new关键字来实例化它。其他所有内容保持不变。

3
非常简洁的结构! - Christophe Marois
1
所以在你的<ES6示例中,似乎使用new和不使用它没有区别。但是,那只是因为你有一个检查this instanceof square吗?如果是这样,那么这个机制到底是做什么的? - arichards
2
以下是我查找的问题,希望对其他人有所帮助: importexport在哪里定义? 这些是ECMAScript 6(ES6)中的保留关键字。在ES6之前,必须使用库来管理模块。Node的模块化是基于CommonJS库的模块进行建模的。 export default Square中的default是什么意思? 这指定了在您只导入“文件”而不是该文件中的其他特定导出时要导入的内容。只要它们存在,我发现这些页面很有帮助:https://spring.io/understanding/javascript-modules 和 http://exploringjs.com/es6/ch_modules.html - arichards

1
这个问题与require()的工作原理没有实质性的关联。基本上,无论你在模块中设置module.exports为什么值,都将被返回给对应require()调用。
这与以下代码等价:
var square = function(width) {
  return {
    area: function() {
      return width * width;
    }
  };
}

调用square函数时不需要使用new关键字。你从square函数中返回的是一个新对象,而不是函数实例本身。因此,你可以直接调用这个函数。

如需了解有关new关键字更复杂的参数,请查看:JavaScript 的 "new" 关键字是否有害?


3
使用new关键字没有任何问题。我讨厌人们对此的恐惧和不确定性。 - Sukima
1
@Sukima 同意。 :-D 我是在指出在这种情况下它并不重要,并链接到另一个关于 new 的问题,以便其他人可以在那里参与对它的争论。 - Brad

0

范例代码如下:

在主函数中

square(width,function (data)
{
   console.log(data.squareVal);
});

使用以下内容可能有效

exports.square = function(width,callback)
{
     var aa = new Object();
     callback(aa.squareVal = width * width);    
}

0
最后,Node关于JavaScript。JS有几种方法来完成某些事情,获取“构造函数”也是同样的事情,重要的是返回一个功能
这种方式实际上创建了一个新的函数,就像我们在Web浏览器环境中使用JS创建的函数一样。
个人而言,我更喜欢原型方法,正如Sukima在Node.js - use of module.exports as a constructor帖子中建议的那样。

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