Node.js: uso de module.exports como constructor

120

Según el manual de Node.js:

Si desea que la raíz de la exportación de su módulo sea una función (como un constructor) o si desea exportar un objeto completo en una asignación en lugar de construir una propiedad a la vez, asígnelo a module.exports en lugar de exportar .

El ejemplo dado es:

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

y se usa así:

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

Mi pregunta: ¿por qué el ejemplo no usa el cuadrado como objeto? ¿Es válido lo siguiente y hace que el ejemplo esté más "orientado a objetos"?

var Square = require('./square.js');
var mySquare = new Square(2);
console.log('The area of my square is ' + mySquare.area());
Naresh
fuente
1
Su ejemplo es un error de sintaxis. Después de cambiar el nombre squarea ya no existe. Squarenew square()
Sukima
3
Lo siento, fue un error tipográfico. Arreglado. Mi intención era mostrar el nombre del objeto / función comenzando con mayúsculas y el nombre de la instancia comenzando con minúsculas.
Naresh
4
Pensé tanto, por eso escribí mi respuesta de la forma en que lo hice. Solo quería decir que estoy muy contento de que otros vean los módulos de la misma manera. A menudo uso la nueva palabra clave y organizo mis módulos para exportar una única función de constructor. Creo que facilita la legibilidad y la conceptualización de las soluciones. Puedo decir de un vistazo qué tipo de construcción pretendo utilizar. Felicitaciones por pensar como yo;)
Sukima

Respuestas:

173

Los módulos CommonJS permiten dos formas de definir propiedades exportadas. En cualquier caso, está devolviendo un objeto / función. Debido a que las funciones son ciudadanos de primera clase en JavaScript, pueden actuar como Objetos (técnicamente son Objetos). Dicho esto, su pregunta sobre el uso de newpalabras clave tiene una respuesta simple: sí. Te ilustraré ...

Exportaciones de módulos

Puede utilizar la exportsvariable proporcionada para adjuntarle propiedades. Una vez que se requieran en otro módulo, esas propiedades de asignación estarán disponibles. O puede asignar un objeto a la propiedad module.exports. En cualquier caso, lo que devuelve require()es una referencia al valor de module.exports.

Un ejemplo de pseudocódigo de cómo se define un módulo:

var theModule = {
  exports: {}
};

(function(module, exports, require) {

  // Your module code goes here

})(theModule, theModule.exports, theRequireFunction);

En el ejemplo anterior module.exportsy exportsson el mismo objeto. Lo bueno es que no ve nada de eso en sus módulos CommonJS, ya que todo el sistema se encarga de eso, todo lo que necesita saber es que hay un objeto de módulo con una propiedad de exportación y una variable de exportación que apunta a la lo mismo que hace module.exports.

Requerir con constructores

Dado que puede adjuntar una función directamente module.exports, esencialmente puede devolver una función y, como cualquier función, podría administrarse como un constructor (eso está en cursiva, ya que la única diferencia entre una función y un constructor en JavaScript es cómo pretende usarlo. Técnicamente no hay diferencia.)

Entonces, el siguiente es un código perfectamente bueno y personalmente lo recomiendo:

// 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"

Requerir para no constructores

Lo mismo ocurre con las funciones que no son de constructor:

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

// In another module
var MyModule = require("./my_module.js");
MyModule.someFunction("foobar"); // => "foobar"
Sukima
fuente
2
¿Puedo requerir ('./ my-object.js') ("foobar") para abreviar? ¿O la sintaxis require ('module') (params) para un caso de uso diferente?
Hampus Ahlgren
1
Nada te detiene, todo es solo JavaScript. Entonces sí, puedes usar la sintaxis más corta.
Sukima
3
El ejemplo de pseudocódigo de cómo se define un módulo ha aclarado completamente mi comprensión del sistema de módulos Node.js. ¡Gracias!
Nitax
130

En mi opinión, algunos de los ejemplos de node.js son bastante artificiales.

Puede esperar ver algo más parecido a esto en el mundo real.

// 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;

Uso

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

Para la gente de ES6

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

export default Square;

Usándolo en ES6

import Square from "./square";
// ...

Cuando use una clase, debe usar la newpalabra clave para instalarla. Todo lo demás se queda igual.

maček
fuente
3
¡Estructura inusualmente concisa!
Christophe Marois
1
Entonces, parece que en su ejemplo de <ES6, no hay diferencia entre usarlo newy no usarlo. Pero, ¿es solo porque tienes ese cheque this instanceof square? Si es así, ¿qué está haciendo exactamente ese mecanismo?
arichards
1
Preguntas que tenía y busqué, en caso de que sea útil para otros: ¿Dónde están importy exportdefinidas? Estas son palabras clave reservadas en ECMAScript 6 (ES6). Antes de ES6, uno tenía que usar bibliotecas para administrar módulos. La modulación del nodo se basa en los módulos de la biblioteca CommonJS. ¿Qué es el defaulten export default Square? Esto especifica qué importar cuando solo importa el 'archivo' y no otras exportaciones específicas de ese archivo. Durante el tiempo que existen, me encontré con estas páginas de ayuda: spring.io/understanding/javascript-modules y exploringjs.com/es6/ch_modules.html
arichards
1

Esta pregunta realmente no tiene nada que ver con cómo require()funciona. Básicamente, lo que sea que establezca module.exportsen su módulo será devuelto por la require()llamada.

Esto sería equivalente a:

var square = function(width) {
  return {
    area: function() {
      return width * width;
    }
  };
}

No es necesario newutilizar la palabra clave al llamar square. No está devolviendo la instancia de la función en sí square, está devolviendo un nuevo objeto al final. Por lo tanto, puede simplemente llamar a esta función directamente.

Para obtener argumentos más complejos new, consulte lo siguiente: ¿Se considera dañina la palabra clave "nueva" de JavaScript?

Puntilla
fuente
3
No hay nada de malo en usar la nueva palabra clave. Odio todo el FUD a su alrededor.
Sukima
1
@Sukima de acuerdo. :-D Estoy señalando por qué no importa en este caso, y vinculado a la otra pregunta con respecto a newque otros puedan participar en la guerra allí.
Brad
0

El código de ejemplo es:

en principal

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

usar lo siguiente puede funcionar

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

Al final, Node trata sobre Javascript. JS tiene varias formas de lograr algo, es lo mismo obtener un "constructor", lo importante es devolver una función .

De esta manera, en realidad, está creando una nueva función, como la que creamos usando JS en el entorno del navegador web, por ejemplo.

Personalmente prefiero el enfoque del prototipo, como sugirió Sukima en esta publicación: Node.js - uso de module.exports como constructor

Josué
fuente