Node.js - uso de module.exports como um construtor

120

De acordo com o manual do Node.js:

Se você deseja que a raiz da exportação do seu módulo seja uma função (como um construtor) ou se deseja exportar um objeto completo em uma atribuição em vez de construí-lo com uma propriedade por vez, atribua-o a module.exports em vez de exportações .

O exemplo dado é:

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

e usado assim:

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

Minha pergunta: por que o exemplo não usa o quadrado como objeto? O seguinte é válido e torna o exemplo mais "orientado a objetos"?

var Square = require('./square.js');
var mySquare = new Square(2);
console.log('The area of my square is ' + mySquare.area());
Naresh
fonte
1
Seu exemplo é um erro de sintaxe. Depois de mudar squarepara Squarea new square()não existir.
Sukima
3
Desculpe, foi um erro de digitação. Corrigido. Minha intenção era mostrar o nome do objeto / função começando com maiúsculas e o nome da instância começando com minúsculas.
Naresh
4
Eu imaginei isso, é por isso que escrevi minha resposta da maneira que fiz. Eu só queria dizer que estou muito feliz que outras pessoas vejam os módulos da mesma maneira. Costumo usar a nova palavra-chave e organizar meus módulos para exportar uma única função de construtor. Acho que facilita a legibilidade e a conceituação de soluções. Posso dizer à primeira vista que tipo de construção pretendo usar. Parabéns por pensar como eu;)
Sukima

Respostas:

173

Os módulos CommonJS permitem duas maneiras de definir propriedades exportadas. Em qualquer caso, você está retornando um objeto / função. Como as funções são cidadãos de primeira classe em JavaScript, elas podem agir exatamente como Objetos (tecnicamente, são Objetos). Dito isso, sua pergunta sobre o uso de newpalavras-chave tem uma resposta simples: sim. Vou ilustrar ...

Exportações de módulo

Você pode usar a exportsvariável fornecida para anexar propriedades a ela. Uma vez exigidas em outro módulo, essas propriedades atribuídas tornam-se disponíveis. Ou você pode atribuir um objeto à propriedade module.exports. Em ambos os casos, o que é retornado por require()é uma referência ao valor de module.exports.

Um exemplo de pseudocódigo de como um módulo é definido:

var theModule = {
  exports: {}
};

(function(module, exports, require) {

  // Your module code goes here

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

No exemplo acima module.exportse exportssão o mesmo objeto. A parte legal é que você não vê nada disso em seus módulos CommonJS, pois todo o sistema cuida disso para você, tudo o que você precisa saber é que existe um objeto de módulo com uma propriedade de exportação e uma variável de exportação que aponta para o mesma coisa que o module.exports faz.

Exigir com construtores

Uma vez que você pode anexar uma função diretamente a module.exports você, pode essencialmente retornar uma função e, como qualquer função, ela poderia ser gerenciada como um construtor (está em itálico, pois a única diferença entre uma função e um construtor em JavaScript é como você pretende usá-lo. Tecnicamente não há diferença.)

Portanto, o código a seguir é perfeitamente bom e eu pessoalmente o encorajo:

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

Exigir para não construtores

A mesma coisa vale para funções do tipo não construtor:

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

// In another module
var MyModule = require("./my_module.js");
MyModule.someFunction("foobar"); // => "foobar"
Sukima
fonte
2
Posso fazer require ('./ my-object.js') ("foobar") para breve? Ou a sintaxe require ('módulo') (params) para um caso de uso diferente?
Hampus Ahlgren
1
Nada o impede, é tudo apenas JavaScript. Então, sim, você pode usar a sintaxe mais curta.
Sukima
3
O exemplo de pseudocódigo de como um módulo é definido esclareceu completamente minha compreensão do sistema de módulo Node.js. Obrigado!
Nitax
130

Na minha opinião, alguns dos exemplos de node.js são bastante artificiais.

Você pode esperar ver algo mais parecido com isso no 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 as pessoas ES6

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

export default Square;

Usando no ES6

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

Ao usar uma classe, você deve usar a newpalavra-chave para instatá-la. todo o resto permanece o mesmo.

Macek
fonte
3
Estrutura incomumente concisa!
Christophe Marois
1
Portanto, parece que em seu exemplo <ES6, não há diferença entre usá-lo newe não usá-lo. Mas, isso é apenas porque você tem aquele cheque this instanceof square? Em caso afirmativo, o que exatamente esse mecanismo está fazendo?
arichards
1
Perguntas que eu tive e procurei, caso seja útil para outras pessoas: Onde estão importe exportdefinidas? Essas são palavras-chave reservadas em ECMAScript 6 (ES6). Antes do ES6, era necessário usar bibliotecas para gerenciar módulos. A modulação do Node é modelada a partir dos Módulos da biblioteca CommonJS. O que está defaultdentro export default Square? Isso especifica o que importar quando você apenas importa o 'arquivo' e não outras exportações específicas desse arquivo. Para contanto que eles existem, eu achei estas páginas votos: spring.io/understanding/javascript-modules e exploringjs.com/es6/ch_modules.html
arichards
1

Esta questão realmente não tem nada a ver com como require()funciona. Basicamente, tudo o que você definir module.exportsem seu módulo será retornado da require()chamada para ele.

Isso seria equivalente a:

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

Não há necessidade de newpalavra - chave ao chamar square. Você não está retornando a própria instância da função square, está retornando um novo objeto no final. Portanto, você pode simplesmente chamar essa função diretamente.

Para obter argumentos mais complexos new, verifique isto: A palavra-chave "nova" do JavaScript é considerada prejudicial?

Brad
fonte
3
Não há nada de errado em usar a nova palavra-chave. Eu odeio todo o FUD em torno disso.
Sukima
1
@Sukima Concordo. :-D Estou apontando por que isso não importa neste caso, e ligado à outra questão a respeito de newque outros possam participar da guerra sobre ele lá.
Brad
0

O código de exemplo é:

no principal

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

usar o seguinte pode funcionar

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

No final, Node é sobre Javascript. JS tem várias maneiras de fazer algo, é a mesma coisa de pegar um "construtor", o importante é retornar uma função .

Desta forma na verdade você está criando uma nova função, como criamos usando JS no ambiente Web Browser por exemplo.

Pessoalmente, eu prefiro a abordagem de protótipo, como Sukima sugeriu neste post: Node.js - uso de module.exports como um construtor

Josué
fonte