I'm writing a simple server for Node.js and I'm using my own class called User which looks like:
function User(socket) {
this.socket = socket;
this.nickname = null;
/* ... just the typical source code like functions, variables and bugs ... */
this.write = function(object) {
this.socket.write(JSON.stringify(object));
}
};
and then later in the process I'm instantiating it a lot:
var server = net.createServer(function (socket) {
/* other bugs */
var user = new User(socket);
/* more bugs and bad practise */
});
Can I move my User class definition to another javascript file and "include" it somehow?
You can simply do this:
user.js
class User {
//...
}
module.exports = User // 👈 Export class
server.js
const User = require('./user.js')
let user = new User()
This is called CommonJS module.
ES Modules
Since Node.js version 14 it's possible to use ES Modules with CommonJS. Read more about it in the ESM documentation.
user.mjs (👈 extension is important)
export default class User {}
server.mjs
import User from './user.mjs'
let user = new User()
Using ES6, you can have user.js:
export default class User {
constructor() {
...
}
}
And then use it in server.js
const User = require('./user.js').default;
const user = new User();
Modify your class definition to read like this:
exports.User = function (socket) {
...
};
Then rename the file to user.js. Assuming it's in the root directory of your main script, you can include it like this:
var user = require('./user');
var someUser = new user.User();
That's the quick and dirty version. Read about CommonJS Modules if you'd like to learn more.
Another way in addition to the ones provided here for ES6
module.exports = class TEST{
constructor(size) {
this.map = new MAp();
this.size = size;
}
get(key) {
return this.map.get(key);
}
length() {
return this.map.size;
}
}
and include the same as
var TEST= require('./TEST');
var test = new TEST(1);
If you append this to user.js:
exports.User = User;
then in server.js you can do:
var userFile = require('./user.js');
var User = userFile.User;
http://nodejs.org/docs/v0.4.10/api/globals.html#require
Another way is:
global.User = User;
then this would be enough in server.js:
require('./user.js');
Related
I encountered a problem, if use methodology: one file - one class in typescript project, as in the example below.
File Greeter.ts
module App {
export class Greeter {
constructor(public greeting: string) { }
public greet() {
return "<h1>" + this.greeting + "</h1>";
}
};
}
File Program.ts
module App {
export class Program {
private _greeter = new Greeter("Hello, world!");
public main() {
document.body.innerHTML = this._greeter.greet();
}
};
}
Result file
var App;
(function (App) {
var Greeter = (function () {
// class code
} ());
App.Greeter = Greeter;
})(App || (App = {}));
var App;
(function (App) {
var Program = (function () {
// class code
} ());
App.Program = Program;
})(App || (App = {}));
// point of entry
var program = new App.Program();
program.main();
As can be seen duplication declaration App. I would like exclude any repetition like this:
var App;
(function (App) {
var Greeter = (function () {
// class code
} ());
App.Greeter = Greeter;
var Program = (function () {
// class code
} ());
App.Program = Program;
})(App || (App = {}));
// point of entry
var program = new App.Program();
program.main();
Otherwise, when a large amount of classes, it turns out a lot of excess code in the output file.
Maybe I'm wrong somewhere?
----Update----
The project is building by gulp-concat and gulp-typescript, maybe exist package allowing avoid this?
example on github
Otherwise, when a large amount of classes, it turns out a lot of excess code in the output file.
Maybe I'm wrong somewhere?
No. This is by design. Basically typescript allows each section to run in isolation.
Warning
Please try and migration to modules . More https://basarat.gitbooks.io/typescript/content/docs/tips/outFile.html
I am learning ES 6 syntax of classes. I come from a C# background, so I apologize if my terminology isn't correct. Or, if I'm doing something that looks odd.
I'm building a web app as a learning exercise. It's built on Node and Express. I have some routes defined like this:
'use strict';
module.exports = function() {
const app = this;
app.use('/blog', function(req, res) {
console.log('loading blog postings');
res.render('blog', {});
});
app.use('/', function(req, res) {
console.log('looking up: ' + req.path);
res.render('home', {});
});
};
I'm trying to put some viewModels behind these views. So, I have a directory called viewModels. That directory has these files:
index.js
blog.js
home.js
The files currently, probably inaccurately, look like this:
index.js
'use strict';
module.exports = function() {
const HomeViewModel = require('./home);
const BlogViewModel = require('./blog);
};
blog.js
export default class BlogViewModel {
constructor() {
this.title = 'My Blog';
}
}
home.js
export default class HomeViewModel {
constructor() {
this.title = 'Home';
}
}
My thought, was that I could use index.js as a way to define my package or namespace. Then, in my routing code, I could do something like this:
'use strict';
module.exports = function() {
const app = this;
const ViewModels = require('../viewModels/index');
app.use('/blog', function(req, res) {
console.log('loading blog postings');
let viewModel = new ViewModels.BlogViewModel();
res.render('blog', viewModel);
});
app.use('/', function(req, res) {
console.log('looking up: ' + req.path);
let viewModel = new ViewModels.HomeViewModel();
res.render('home', viewModel);
});
};
However, when I attempt this, I get some runtime errors that say "Error: Cannot find module '../viewModels/index'". This implies that I'm not setting up my module properly. But, it looks like I am what am I doing wrong?
Your index.js file is incorrect, you don't export ViewModels from there. Change it to this:
'use strict';
module.exports = {
HomeViewModel: require('./home'),
BlogViewModel: require('./blog')
};
And ... viewModels it's good for C#, but not for Node.js. In node it should be just models, IMO.
Update:
Node.js doesn't completely support all ES6 features, especially new modules declaration: https://nodejs.org/en/docs/es6/. You should use the standard CommonJs module declaration to export your function:
'use strict';
class HomeViewModel {
constructor() {
this.title = 'Home';
}
}
module.exports = HomeViewModel;
Actually, I am not really sure what you want to ask. If I answered wrong stuff, just never mind.
First, the reason why you get error Error: Cannot find module '../viewModels/index' is because you put two dots there. It should be just one dot means start from here. However, I am not sure if this is the problem. I wanted to ask where you put routing code, but I do not have permission to comment yet. (Ahhhh.... Stack overflow you kidding me...)
Second, here is the proper way to export a class in ES6.
For example:
AClass.js
'use strict';
//This module can be used within the class. However, you cannot use it in another file.
const AModule = require('AModule');
//The class name used here just for debug output.
module.exports = class AClass {
constructor(startValue) {
//Not like C#. JavaScript does not define private or public.
this.value = startValue;
}
method(incValue) {
this.value += incValue;
AModule(); //Just show you can use this module within the class;
}
}
main.js
'use strict';
//You need to put a ./ before the path if you are include another module made by yourself.
//You do not need it for a npm module.
const AClass = require('./AClass.js');
//Now you just required the class of the AClass, you still need to new one;
var aClass = new AClass(500);
//After new one, you can call its method.
aClass.method(30);
//You can access its property by a dot;
console.info(aClass.value); //530
This is the 100% work way to create a class in ES6.
Here is detailed documentation:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
Anyways, classes in JavaScripts are just like functions, and a function's prototype will be its methods. new Class() is just create a object, run the function Class with object as this (Same as Class.bind(obj, parameters)), then link the new object's constructor property to the function used.
module.exports = xxx is just make the xxx the value of this module. For instance, if you module.exports = 'Hello'; and console.info(require('module'));, you will get Hello.
I have the following module/class and submodule setup
MyAPI.js
class MyAPI {
construction(){
this.food = require('./Food');
}
}
module.exports = MyAPI;
Food.js
class Food {
constructor(){
...
}
}
module.exports = Food;
app.js
var api = require('./MyAPI');
var taco = new api.food;
var cheeseburger = new api.food;
What I'm wondering, is it possible to call upon MyAPI properties and functions form within Food.js? Do I need to pass this into the require somehow?
this.food = require('./Food')(this); // this didn't work...
The above resulted in this:
TypeError: Class constructors cannot be invoked without 'new'
But why would I use new in the MyAPI constructor?
What is the best approach here to do subclasses and submodules and creating new objects from them?
I think you are confusing classes and instances:
var MyAPI = require('./MyAPI');//this is a class
var apiInstance = new MyAPI();//the creates a new instance of your class
var taco = new apiInstance.food //the food property on your api is a class not an instance
var tacoInstance = new taco();
this.food is assigned in the constructor of MyApi, so you will need to instantiate MyApi to have the property accessible.
var Api = require('./MyAPI');
var apiInstance = new Api();
var foodInstance = new apiInstance.food();
From your comment, it seems like you want properties of MyApi, particularly config to be accessible by submodules. I don't see a way of doing this except to make your top level API object a singleton:
var MyAPI = {
config: { setting: 'default' },
Food: require('./Food')
}
module.exports = MyAPI;
var MyApi = require('./my-api.js');
class Food {
constructor(){
// MyApi.config
}
}
module.exports = Food;
Looking at the AWS source they are doing something similar (except config is it's own module mounted on the top level AWS object).
I have read a lot of articles about how to create modules in node.js, and you can use module.exports to expose module internals to the file which includes it.. awesome!
How does this work the other way around? I'll use the following as an example :-)
USER.JS
function User() {
this.property = 'value';
this.doSomething = function() {
var getStuff = mainFileFunction();
// do something with getStuff
}
module.exports = User;
MAIN.JS
var myArray = [];
myArray.push('something');
myArray.push('something else');
mainFileFunction() {
for(thing in myArray) {
return myArray[thing];
}
}
var u = new user();
log(u.property); <--- THIS IS EXPOSED, COOL!
u.doSomething(); <--- This will throw an error because mainFileFunction is not defined in user.js :-(
If I were to move mainFileFunction to the user file, then it still wouldn't work because the myArray array wouldn't be defined... and if I were to move that over too, I wouldn't be able to use it in other functions in main (which I want to) :-)
I'm sorry if I'm missing something really obvious here... What I want is to expose the parts of my choosing from modules I include (module.export works for that) but I also want to expose everything from the main file to all the includes..
or just expose everything to everything? is that totally messy and horrible??
Just to explain what I am trying to do here... I want to have classes defined in separate files, but I want to instantiate a bunch of them as objects in the main file and store them in arrays.. I want the objects to contain methods which can access arrays of the other object types.
Thanks guys! :-)
You can use globals, or have a proper circular dependency (requireing both files), however - this is usually a bad habit which can lead to maintainability problems in the future.
Instead, you can use dependency injection and inject doSomething into your module.
This basically gives you the following for free:
You can test User with a simple mock implementation of doSomething later and verify the correctness of your code
The dependencies of a user are explicit and not implicit, which makes it obvious what a user needs.
I'll provide two implementations, one using constructor dependency injection and one with a module wide setting.
USER.JS
function User(dependentFunction) {
this.property = 'value';
this.doSomething = function() {
var getStuff = dependentFunction();
// do something with getStuff
}
}
module.exports = User;
MAIN.JS
...
var u = new User(mainFileFunction);
u.doSomething(); // this will now work, using mainFileFunction
What happens here is fairly simple, and we know what's going on.
This can also be a module wide setting
USER.JS
function User(depFunc) {
this.property = 'value';
this.doSomething = function() {
var getStuff = depFunc();
// do something with getStuff
}
}
function UserFactory(depFunc){
return function(){
return new User(depFunc);
}
}
module.exports = UserFactory;
MAIN.JS
var getUser = UserFactory(mainFileFunction);
var u = getUser(); // will return a new user with the right function
+1 to Benjamin answer for dependency injection.
I would like to add another way to inject objects in your modules by passing the dependency in the require like require('./module.js')(dependentFunction);
//MAIN.js
var db = function() {
this.rows = [];
this.insert = function(name) {
this.rows.push(name);
console.log('Db: Inserting user with name ' + name);
}
this.getAll = function(){
return this.rows;
}
}
var fakeDb = new db();
var user = require('./user.js')(fakeDb);
user.add('Jhon');
user.add('Rose');
user.list();
and
//users.js
module.exports = function(db) {
return {
add: function(name) {
db.insert(name);
},
list: function() {
var users = db.getAll();
var i = users.length;
console.log('listing users\n-------');
while(i--) {
console.log(users[i]);
}
}
}
}
You should pass mainFileFunction as a parameter to the constructor of user.
USER.JS
function User(mainFileFunction) {
this.property = 'value';
this.doSomething = function() {
var getStuff = mainFileFunction();
// do something with getStuff
}
module.exports = User;
In your main.js use the following
var u = new user(mainFileFunction);
How about moving mainFileFunction to user.js, and have the function accept an array as an argument:
mainFileFunction(array) {
for(thing in array) {
return array[thing];
}
}
And then when you call it from main.js, pass the function your array:
u.doSomething(myArray);
I've been trying for the last hour to write a user module for passport.js with the findOne, findOneOrCreate, etc. methods, but can't get it right.
User.js
var User = function(db) {
this.db = db;
}
User.prototype.findOne(email, password, fn) {
// some code here
}
module.exports = exports = User;
app.js
User = require('./lib/User')(db);
User.findOne(email, pw, callback);
I've been through dozens of errors, mostly
TypeError: object is not a function
or
TypeError: Object function () {
function User(db) {
console.log(db);
}
} has no method 'findOne'
How do I create a proper module with these functions without creating an object/instance of User?
Update
I went over the proposed solutions:
var db;
function User(db) {
this.db = db;
}
User.prototype.init = function(db) {
return new User(db);
}
User.prototype.findOne = function(profile, fn) {}
module.exports = User;
No luck.
TypeError: Object function User(db) {
this.db = db;
} has no method 'init'
A couple of things are going on here, I've corrected your source code and added comments to explain along the way:
lib/User.js
// much more concise declaration
function User(db) {
this.db = db;
}
// You need to assign a new function here
User.prototype.findOne = function (email, password, fn) {
// some code here
}
// no need to overwrite `exports` ... since you're replacing `module.exports` itself
module.exports = User;
app.js
// don't forget `var`
// also don't call the require as a function, it's the class "declaration" you use to create new instances
var User = require('./lib/User');
// create a new instance of the user "class"
var user = new User(db);
// call findOne as an instance method
user.findOne(email, pw, callback);
You need to new User(db) at some point.
You could make an init method
exports.init = function(db){
return new User(db)
}
And then from your code:
var User = require(...).init(db);