Node.js - использование module.exports в качестве конструктора

120

Согласно руководству по Node.js:

Если вы хотите, чтобы корнем экспорта вашего модуля была функция (например, конструктор), или если вы хотите экспортировать полный объект в одном назначении, а не строить его по одному свойству за раз, назначьте его для module.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());

Мой вопрос: почему в примере не используется квадрат в качестве объекта? Верно ли следующее и делает ли пример более «объектно-ориентированным»?

var Square = require('./square.js');
var mySquare = new Square(2);
console.log('The area of my square is ' + mySquare.area());
Naresh
источник
1
Ваш пример - синтаксическая ошибка. После переименования не squareв больше не существует. Squarenew square()
Sukima
3
Извините, это была опечатка. Починил это. Я намеревался показать имя объекта / функции, начинающееся с верхнего регистра, а имя экземпляра - с нижнего регистра.
Нареш
4
Я так и думал, поэтому и написал свой ответ именно так. Я просто хотел сказать, что очень рад, что другие смотрят на модули так же. Я часто использую ключевое слово new и организую свои модули для экспорта одной функции конструктора. Я считаю, что это упрощает читаемость и концептуальную концепцию решений. Я могу сразу сказать, какую конструкцию я собираюсь использовать. Престижность за то, что думаешь, как я;)
Sukima

Ответы:

173

Модули 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.exportsи exportsэто тот же объект. Крутая часть заключается в том, что вы не видите ничего из этого в своих модулях CommonJS, поскольку вся система заботится об этом для вас. Все, что вам нужно знать, это объект модуля со свойством экспорта и переменная экспорта, которая указывает на то же самое, что и module.exports.

Требовать с конструкторами

Поскольку вы можете прикрепить функцию непосредственно к 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"
Sukima
источник
2
Могу ли я для краткости потребовать ('./ my-object.js') ("foobar")? Или нужен синтаксис require ('module') (params) для другого варианта использования?
Хампус Альгрен
1
Ничто вас не останавливает, это всего лишь JavaScript. Так что да, вы можете использовать более короткий синтаксис.
Sukima
3
Пример псевдокода того, как определяется модуль, полностью прояснил мое понимание модульной системы Node.js. Спасибо!
Nitax
130

На мой взгляд, некоторые примеры 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ключевое слово для его инсталляции. Все остальное остается прежним.

Maček
источник
3
Необычайно лаконичная структура!
Christophe Marois
1
Итак, похоже, что в вашем примере <ES6 нет разницы между его использованием newи неиспользованием . Но это только потому, что у вас есть чек this instanceof square? Если да, то что именно делает этот механизм?
Arichards,
1
Вопросы, которые у меня были, и которые я искал, на случай, если это будет полезно другим: Где находятся importи exportопределены? Это зарезервированные ключевые слова в ECMAScript 6 (ES6). До ES6 для управления модулями приходилось использовать библиотеки. Модулирование узла моделируется по образцу модулей библиотеки CommonJS. Что defaultвнутри export default Square? Это указывает, что нужно импортировать, когда вы просто импортируете «файл», а не другие, определенные операции экспорта из этого файла. Для тех пор , пока они существуют, я нашел эти страницы полезные: spring.io/understanding/javascript-modules и exploringjs.com/es6/ch_modules.html
arichards
1

Этот вопрос на самом деле не имеет ничего общего с тем, как require()работает. По сути, все, что вы установили module.exportsв своем модуле, будет возвращено для этого require()вызова.

Это было бы эквивалентно:

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

newПри звонке ключевое слово не требуется square. Вы не возвращаете сам экземпляр функции, squareв конце вы возвращаете новый объект. Следовательно, вы можете просто вызвать эту функцию напрямую.

Чтобы newнайти более сложные аргументы , проверьте это: считается ли ключевое слово JavaScript "new" вредоносным?

штифтик
источник
3
Нет ничего плохого в использовании ключевого слова new. Я ненавижу всю эту FUD вокруг этого.
Sukima
1
@Sukima Согласен. :-D Я указываю, почему это не имеет значения в данном случае, и связан с другим вопросом, касающимся того, newчтобы другие могли участвовать в войне там.
Брэд
0

Пример кода:

в основном

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

использование следующих может работать

exports.square = function(width,callback)
{
     var aa = new Object();
     callback(aa.squareVal = width * width);    
}
AmirtharajCVijay
источник
0

В конце концов, Node посвящен Javascript. JS имеет несколько способов сделать что-то, то же самое, чтобы получить «конструктор», важно вернуть функцию. .

Таким образом, вы фактически создаете новую функцию, как мы, например, создали с помощью JS в среде веб-браузера.

Лично я предпочитаю прототипный подход, как предложил Сукима в этом посте: Node.js - использование module.exports в качестве конструктора

Josue
источник