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());

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"

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.


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?


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);    
}

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.