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).
Related
I have a bit of code like
var myClass = function(myArray) {
this.arr = array;
}
myClass.prototype.myMethod = function () {
//random code
};
module.exports = myClass;
When I start node from the console, require this file with var a = require('./myClass.js') and try to instantiate my class with
myclass = new myClass([1,2,3]);
what I get is ReferenceError: myClass is not defined. I am building a very small and easy game, I wonder how I would go about testing it and playing around with it from the node console. Thanks.
When you do this:
var a = require('./myClass.js')
a becomes the thing you exported from 'myClass.js'. You would need to use it like:
var instance = new a([1, 2, 3])
You can do that, but it's probably easier to read and understand if you use a more descriptive variable name:
var myClass = require('./myClass.js')
// myClass is the function exported in `myClass.js`
var someInstance = new myClass([1,2,3]);
Also, you probably want to use myArray rather than array here:
var myClass = function(myArray) {
this.arr = myArray // not array;
}
With ES5 constructor and prototype approach I can add public (prototype) properties as below:
function Utils(){}
Utils.prototype.data = {};
var utils = new Utils();
console.log(utils.data); //{}
The ES6 class allows me to define only public methods in the class. I build an app with a class-approach and I don't want to mix constructors and classes features. The working code that I figured out is:
class Utils(){
get _data(){
const proto = Object.getPrototypeOf(this);
if(!proto._status) proto._data = {};
return proto._data;
}
}
const utils = new Utils();
console.log(utils._data); //{}
When I call _data getter method, it checkes whether the _data property exists in the prototype object. If so, it returns it, otherwise it initiates the _data property.
Is it a good practice? Is there any other way to do it better?
To make data a public instance property:
class Utils {
constructor () {
this.data = {}
}
}
To make data a public static property, get/set is probably the best way:
let data = {}
class Utils {
get _data () {
return data
}
set _data (d) {
data = d
}
}
I don't know if the code you provided is your full code or not, but when I run it, it throws an error:
class Utils {
get _data(){
const proto = Object.getPrototypeOf(this);
if(!proto._status) proto._data = {};
return proto._data;
}
}
/* TEST */
const a = new Utils();
a._data.ok = 'ok';
const b = new Utils();
console.log(b._data.ok);
If I understand you correctly, you want all instances of Utils to share the same data property.
There is a few ways that I can think of to do what you need, but it might "mix constructors and classes features" (I don't really get what you mean by that).
1: Good ol' ES5 way
class Utils {}
Utils.prototype.data = {};
/* TEST */
const a = new Utils();
a.data.ok = 'ok';
const b = new Utils();
console.log(b.data.ok);
2: Same as your way, but in it's constructor
class Utils {
constructor(){
if (!this.data) {
Utils.prototype.data = {};
}
}
}
/* TEST */
const a = new Utils();
a.data.ok = 'ok';
const b = new Utils();
console.log(b.data.ok);
Though, as the data property needs to be shared across instances, I'd suggest you to add the property to its prototype using Object.defineProperty method to make it unwritable and unconfigurable:
Object.defineProperty(Utils.prototype, 'data', {
value: {},
writable: false,
enumerable: true,
configurable: false
});
This is to ensure the data property cannot be reassigned or deleted, thus minimising the chance of mistakenly reset the data or etc.
I'd recommend the first way (with Object.defineProperty) because it is :
More foolproof
Clearer
Easier to maintain
How JS code should be structered when instantiating new classes inside controller class Main.
Solutions:
A: pass arguments while creating new class - new Options(args) - and let Options's constructor call its own methods.
B: create new class and call the classes' methods on the object.
Later I'd use properties from Options in another classes.
// A
class Main {
constructor(options) {
this.options = new Options(options);
{ firstProperty, secondProperty } = this.options;
this.another = new Another(firstProperty, secondProperty);
}
}
// B
class Main {
constructor(options) {
this.options = new Options();
const firstProperty = this.options.methodA(options);
const secondProperty = this.options.methodB(options);
this.another = new Another();
const anotherPropety = this.another.methodA(firstProperty);
(...)
}
}
For the purposes of decoupling I would suggest a third option.
//main.js
class Main {
constructor(options) {
this.options = options;
// all instances of class would have:
this.options.foo = 'bar'
}
method() {
return `${this.options.foo} - ${this.options.setup}`
}
}
// example.js
const options = new Options({setup: 'options'});
const example = new Main(options);
console.log(example.method());
This lets your inject your dependencies into a given class, which makes writing tests for your code far simpler. It also gives you the benefit of (as long as you maintain a common interface) swapping out Options for NewAwesomeOptions at some later point without having to find everywhere you might have hard coded it into a class.
The following code works perfectly... but it would seem using __proto__ is considered controversial. Is this true in the confines of Protractor/Nodejs? And if so, how else could I accomplish the same thing?
Given a basePage:
var BasePage = function() {
this.to = function() {
browser.get(this.url);
};
};
module.exports = new BasePage;
And a page that would extend BasePage:
var basePage = require('../pages/basePage.js');
var MyPage = function() {
this.__proto__ = basePage; // extend basePage...
this.url = 'http://myPage.com';
};
module.exports = new MyPage;
When a test calls:
var myPage = require('../pages/myPage.js');
it('should go to page', function() {
myPage.to();
};
Then win?
but it would seem using__proto__ is considered controversial.
Yes.
Is this true in the confines of Protractor/Nodejs?
Yes, even though at least in the known environment you can be sure that it works.
And if so, how else could I accomplish the same thing?
There is no reason to set the __proto__ in the constructor like you do. That's what the .prototype property was made for! This will work exactly like your code:
var basePage = require('../pages/basePage.js');
var MyPage = function() {
this.url = 'http://myPage.com';
};
MyPage.prototype = basePage; // extend basePage...
module.exports = new MyPage;
However, it's a bit strange that you export instances of your constructor. If your goal is to create singleton objects, don't use constructors and new. If your goal is to create a "class", you should export the constructor function (and do inheritance a bit different).
In the snippet below you can play around with different ideas involving prototypal inheritance. My personal take is that it's more conventional to call the base class constructor inside the subclass constructor. That way you can use your code in any browser as well as in Node.
var baseDiv = document.getElementById("base");
var subDiv = document.getElementById("sub");
var BaseClass = function BaseClassConstructor(div) {
this.div = div;
};
BaseClass.prototype.text = "I'm the base class!";
BaseClass.prototype.to = function BaseClassTo() {
this.div.innerHTML = this.text;
}
// This SubClass calls the base class constructor on its "this" context.
var SubClass = function SubClassConstructor(div) {
BaseClass.call(this, div);
};
// The prototype is then constructed by cloning the base class prototype.
SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.text = "I'm the sub class!";
var b = new BaseClass(baseDiv);
var s = new SubClass(subDiv);
s.to();
b.to();
<div id="base"></div>
<div id="sub"></div>
I have a class that inherits from another. Within the base class, I'm wondering if it is possible to create and return a new instance of the calling parent class.
Here's an example:
Base:
var util = require('util');
var ThingBase = function(options) {
}
ThingBase.prototype.mapper = function(data) {
// do a bunch of stuff with data and then
// return new instance of parent class
};
Parent:
var FooThing = function(options) {
ThingBase.call(this, options);
};
util.inherits(FooThing, ThingBase);
FooThing.someMethod = function() {
var data = 'some data';
var newFooThing = this.mapper(data); // should return new FooThing instance
};
The reason why I wouldn't just create a new instance from someMethod is that I want mapper to do a bunch of stuff to the data before it returns an instance. The stuff it will need to do is the same for all classes that inherit from Base. I don't want to clutter up my classes with boilerplate to create a new instance of itself.
Is this possible? How might I go about achieving something like this?
Assuming that FooThing.prototype.constructor == FooThing, you could use
ThingBase.prototype.mapper = function(data) {
return new this.constructor(crunch(data));
};
All other solutions would "clutter up [your] classes with boilerplate [code]", yes, however they don't rely on a correctly set constructor property.