How to create functions base on config in javascript class - javascript

I can defined readFoo in Foo class:
var myFormat = 'foo'
class Foo {
[ "read" + ((format) => format)(myFormat) ]() {
return 123;
}
}
is there any way how to define function base on config like:
var config = ['foo1', 'foo2']
class Foo {
config.map((name) => {
[ "read" + ((format) => format)(name) ]() {
return 123;
}
}
}
Will create functions readFoo1 and readFoo2.

You can iterate through the array and assign to the prototype afterwards:
var config = ['foo1', 'foo2']
class Foo {}
for (const name of config) {
Foo.prototype["read" + name] = function() {
return 123;
};
}
const f = new Foo();
console.log(f.readfoo1());

Related

For ... in not yielding methods

Given the following:
export class MyClass {
public dataA = 0
private dataB = 123
public myMethod(): any {
return {
test: 'true'
}
}
constructor() {
for (const propOrMethod in this) {
console.log({propOrMethod})
}
}
}
const myInst = new MyClass()
I run this with ts-node index.ts and all i get is:
{ propOrMethod: 'dataA' }
{ propOrMethod: 'dataB' }
With no reference to myMethod. I would like to iterate over all the methods of my class, but they don't appear to exist
for..in iterates over all enumerable properties of the instance and up the prototype chain. But normal methods in a class are not enumerable:
class MyClass {
myMethod() {
return {
test: 'true'
};
}
}
console.log(Object.getOwnPropertyDescriptor(MyClass.prototype, 'myMethod').enumerable);
So it doesn't get iterated over.
If you want to iterate over non-enumerable properties as well, use Object.getOwnPropertyNames (which iterates over the object's own property names, so you'll need to do so recursively if you want all property names anywhere in the prototype chain):
const recurseLog = obj => {
for (const name of Object.getOwnPropertyNames(obj)) {
console.log(name);
}
const proto = Object.getPrototypeOf(obj);
if (proto !== Object.prototype) recurseLog(proto);
};
class MyClass {
dataA = 0;
dataB = 123;
constructor() {
recurseLog(this);
}
myMethod() {
return {
test: 'true'
};
}
}
const myInst = new MyClass();
You could also make the method enumerable:
class MyClass {
dataA = 0;
dataB = 123;
constructor() {
for (const propOrMethod in this) {
console.log({propOrMethod})
}
}
myMethod() {
return {
test: 'true'
};
}
}
Object.defineProperty(MyClass.prototype, 'myMethod', { enumerable: true, value: MyClass.prototype.myMethod });
const myInst = new MyClass();
Or assign the method after the class definition:
class MyClass {
dataA = 0;
dataB = 123;
constructor() {
for (const propOrMethod in this) {
console.log({propOrMethod})
}
}
}
MyClass.prototype.myMethod = () => ({ test: 'true' });
const myInst = new MyClass();
Or assign it to the instance in the constructor:
class MyClass {
dataA = 0;
dataB = 123;
constructor() {
this.myMethod = this.myMethod;
for (const propOrMethod in this) {
console.log({propOrMethod})
}
}
myMethod() {
return {
test: 'true'
};
}
}
const myInst = new MyClass();

ES6 pass an object to constructor and set property

Look at the below example:
class Parent {
constructor({ parentOnlyArg = 'default value' } = {}) {
this.parentOnlyArg = parentOnlyArg;
}
}
class Child extends Parent {
// this class and also any class inheriting from it
constructor({ visibleStyle = 'inline' } = {}) {
// I want to pass argument to super as an object
super(/** args **/);
this.visibleStyle = visibleStyle;
}
}
class Child2 extends Parent {
// Specifying parentOnlyArg as default will change the behaviour
constructor({ parentOnlyArg = 'another parent value',
someOther = 'value' } = {}) {
// I want to pass argument to super as an object
super(/** args **/);
this.someOther = someOther;
}
}
Is it possible to pass on the constructor argument to super?
Seems like it was simpler than I thought
super(...arguments);
I can then create Child using
var c1 = new Child(); // c.parentOnlyArg = 'default value'
var c2 = new Child2(); // c.parentOnlyArg = 'another parent value'
var c3 = new Child({ parentOnlyArg: 'changed again' }); // c.parentOnlyArg = 'changed again'
You could use object destructuring with rest properties. It is not yet implemented by browsers, but BabelJs can transpile it.
function assertEmpty(obj) {
if (Object.keys(obj).length > 0) {
throw new Error("Unexpected parameters");
}
}
class A {
constructor({ a = "foo", ...rest } = {}) {
assertEmpty(rest);
console.log("new A " + a);
}
}
class B extends A {
constructor({ b = "bar", ...rest } = {}) {
super(rest);
console.log("new B " + b);
}
}
new B({a:2}); // prints 'new A 2', 'new B bar'
new B({a:4, b:5, c:6}); // throws 'Unexpected parameters'
In the above snippet parent classes don't see the params consumed by the descendants. If you have problems with that you can do it either as #Bergi or #loganfsmyth suggested. For example:
class A {
constructor(params = {}) {
const { a = "foo" } = params;
console.log("a=" + a);
}
}
class B extends A {
constructor(params = {}) {
const { b = "bar" } = params;
super(params);
console.log("b=" + b);
}
}
new B({a:2}); // prints a=2 b=bar
new B({b:5}); // prints a=foo b=5
A quick-win is to use the arguments object. It is an array containing all parameters passed to a function.
More information on the MDN.
In practice, you can access to the first parameter of your function thanks to arguments[0].
class Child extends Parent {
constructor({ parentOnlyArg = 'value',
visibleStyle = 'inline' } = {}) {
super(arguments[0]);
[...]
}
}

Module variable as output of a method

I have a Javascript module:
const myModule = {
foo: this.initializeFoo(),
initializeFoo(){
// some loops and stuff to create an array
}
}
But I get an error: this.initializeFoo is not a function.
Is there some syntax I need to use to make this work, or is it not possible?
If you only intend to call it once and at the object's creation, then I would opt for a self-executing anonymous function:
const myModule = {
foo: (function () {
// some loops and stuff to create an array
})()
};
Alternatively, you can use arrow syntax instead:
const myModule = {
foo: (() => {
// some loops and stuff to create an array
})()
}
Example snippet:
const myModule = {
foo: (() => {
console.log('Processing');
return Array.apply(null, {length: 10}).map(Number.call, Number);
})()
};
// You can see that 'Processing' is only printed once
console.log(myModule.foo[2]);
console.log(myModule.foo[7]);
Declare the initializeFoo() function in outside of the Object
const myModule = {
foo : initializeFoo(),
}
function initializeFoo(){
return 'hi';
//or some loops and stuff to create an array
}
console.log(myModule)
If you can use ES6:
class MyModule {
constructor() {
this.initializeFoo();
}
initializeFoo() {
console.log('test');
// some loops and stuff to create an array
}
}
const myModule = new MyModule();
If you need to store the result of initializeFoo():
class MyModule {
constructor() {
this.initializeFoo();
}
initializeFoo(){
console.log('test');
this.initialVal = 2;
}
}
const myModule = new MyModule();
// 'test'
myModule.initialVal
// 2

Acess to this from subobject in JavaScript

How do I get access to the properties or method of the main object, from sub-obiect level two (sub3). If possible I would like to avoid solutions chaining return this.
Obj = function () {};
Obj.prototype = {
name: 'name',
main: function(){
console.log(this.name);
},
subobject: {
sub2: function () {
console.log(this);
},
sub3: function () {
console.log(this.name); // How access to Obj.name ??
}
}
}
o = new Obj();
o.main(); // return name
o.subobject.sub2(); // return subobject
o.subobject.sub3(); // return undefined
With your current syntax, you can't. Because for sub2 and sub3, the this variable is Obj.prototype.subobject.
You have multiple choice:
The obvious one: don't use a suboject.
Create subobject, sub2 and sub3 in the constructor
Obj = function() {
var self = this;
this.subobject = {
sub1: function() { console.log(self); }
}
}
Use bind at each call:
o.subobject.sub2.bind(o)();

How do you pull out common methods that depend on local variables in Javascript?

I'm using Node.js and am creating some models for my different objects. This is a simplified version of what they look like at the moment.
var Foo = module.exports = function () {
var values = { type: 'foo', prop1: '', prop2: '' };
function model() {}
model.init = function(val) {
_.extend(values, val);
return model;
}
model.store = function(cb) {
db.insert(values.type, values, cb);
}
model.prop1 = function(val) {
if(!arguments.length) return values.prop1;
values.prop1 = val;
return model;
}
return model;
}
Then I can do:
var foo = Foo();
foo.init({prop1: 'a', prop2: 'b'}).store(function(err) { ... });
A lot of the functions, like model.init and model.store are going to be identical for every model, but they depend on local variables in the closure like values.
Is there a way to pull these functions into a base class that I can then extend each of models with instead of duplicating all of this code? I would like to end up with something like this, but I'm not sure what the base class should look like or the right way to use it to extend Foo.
var Foo = module.exports = function () {
var values = { type: 'foo', prop1: '', prop2: '' };
function model() { this.extend(base); }
model.prop1 = function(val) {
if(!arguments.length) return values.prop1;
values.prop1 = val;
return model;
}
return model;
}
Yes you could do something like this;
model.js
/** require your db and underscore varialbles etc.. **/
module.exports = function(values, base) {
var model = typeof base == 'function' ? base : function() {};
model.init = function(val) {
_.extend(values, val);
return model;
}
model.store = function(cb) {
db.insert(values.type, values, cb);
}
return model;
}
then the usage would be similar to;
var Foo = module.exports = function () {
var values = { type: 'foo', prop1: '', prop2: '' };
var model = require('/path/to/model.js')(values);
model.prop1 = function(val) {
if(!arguments.length) return values.prop1;
values.prop1 = val;
return model;
}
return model;
}
If you need to extend the constructor
var Foo = module.exports = function () {
var values = { type: 'foo', prop1: '', prop2: '' },
model = function() { ...something here... };
require('/path/to/model.js')(values, model);
model.prop1 = function(val) {
if(!arguments.length) return values.prop1;
values.prop1 = val;
return model;
}
return model;
}

Categories