0

I want to create a modular class. It should be usable like following example and should be seperatable into files. The sub class (Tasks) should have access to the methods of the parent class (Foo).

// Use basic methods
const foo = new Foo();
foo.connect();
foo.authenticate('user', 'password');

// Use task (or any other) module 
const tasks = foo.Tasks;
tasks.createTask('ask on stackoverflow');
tasks.updateTask({ done: true });

I only got the following working. But because I have to initiate a new instance of Bar with the new keyword I can't access the changed value of this.output. How can I omit the new keyword and use the same instance as foo with my desired syntax above?

const Foo = class Foo {
  constructor() {
    this.output = 1;
  }

  changeOutput(output) {
    this.output = output;
  }
}

Foo.Bar = class Bar extends Foo {
  getOutput() {
    return this.output;
  }
}

const foo = new Foo();
foo.changeOutput(2);

const bar = new Foo.Bar(); // Creates a new instance what is wrong, but foo.Bar() or anything else doesn't work (Error: is not a function).
console.log(bar.getOutput()); // Result is 1, but should be 2
Hativ
  • 1,500
  • 1
  • 16
  • 24
  • 1
    You created two separate instances of the class, why would the output value be shared? – loganfsmyth Aug 22 '17 at 21:06
  • It was the only way to get my desired syntax working. I don't want two separate instances, it should be one. That's why I'm asking. – Hativ Aug 22 '17 at 21:12
  • @Hativ Wat? If you don't want two instances, don't use `new` twice. Just use `foo = bar`. – Bergi Aug 22 '17 at 21:24
  • @Bergi I know, but without `new` I just get javascript erros. Where should I do `foo = bar`? Can you give me an example? – Hativ Aug 22 '17 at 21:28
  • @Hativ See the "inheritance" part of my answer. – Bergi Aug 22 '17 at 21:31

3 Answers3

1

I have never seen any object oriented language work in the way you are trying, neither will javascript.

Foo.Bar is a static method on Foo, that is extending the class Foo, not the instance foo. That is why you have the number 1 appear.

Seperate your concerns, and apply the change to the instance you are using, the one with the tasks.

class Bar extends Foo {
  getOutput() {
    return this.output;
  }
}
var bar = new Bar();
bar.changeOutput(2)
bar.getOutput() //2

But..

You can edit the binding of the this to achieve what you are looking for, but it is not a good practice to follow a bad OOP design pattern in the first place (you can achieve what you want through other paradigms or by merging the two classes together).

const Foo = class Foo {
  constructor() {
    this.output = 1;
  }

  changeOutput(output) {
    this.output = output;
  }
}

Foo.Bar = class Bar extends Foo {
  getOutput() {
    return this.output;
  }
}

const foo = new Foo();
foo.changeOutput(2);

const bar = new Foo.Bar();

console.log(bar.getOutput.call(foo)); // Result is 2 now
Bamieh
  • 10,358
  • 4
  • 31
  • 52
  • Can you name other paradigms? – Hativ Aug 22 '17 at 21:19
  • 1
    you can use functional programming paradigm (check the partial apps and higher order functions), feature oriented programming (check featuremonkey for js), and traits programming paradigm (there is a javascript library for applying this paradigm but i cant remember it). you can achieve it with OOP, but still, not in this criteria. – Bamieh Aug 22 '17 at 21:41
1

I still have no idea what you are looking for, but it seems to be either

Inheritance

class Foo {
  constructor() {
    this.output = 1;
  }
  changeOutput(output) {
    this.output = output;
  }
}

class Bar extends Foo {
  getOutput() {
    return this.output;
  }
}

const x = new Bar();
x.changeOutput(2);
console.log(x.getOutput());

or

Composition

class Foo {
  constructor() {
    this.output = 1;
    this.bar = new (new.target).Bar(this);
  }
  changeOutput(output) {
    this.output = output;
  }
}

Foo.Bar = class Bar {
  constructor(foo) {
    this.foo = foo;
  }
  getOutput() {
    return this.foo.output;
  }
}

const foo = new Foo();
foo.changeOutput(2);

const bar = foo.bar;
console.log(bar.getOutput());
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • For the second example I get an error "Maximum call stack size exceeded". – Hativ Aug 22 '17 at 21:40
  • 1
    @Hativ Fixed. Apparently `new` binds stronger than the property access on `new.target`, which is kinda weird but well. (I'll have to check whether it's a bug somewhere). One could also have just used `new Foo.Bar(this)`. – Bergi Aug 22 '17 at 21:55
  • 1
    @Hativ [It's a Chrome bug](https://bugs.chromium.org/p/v8/issues/detail?id=6745). But I guess the parenthesis make it easier understand for the human reader as well, so I'll leave them in. – Bergi Aug 23 '17 at 04:07
  • Thank you! I have found something similar to what I want called "Stateful mixins", except I want to use the sub class as prefix. See https://jsfiddle.net/bb8majws/ – Hativ Aug 23 '17 at 21:42
  • 1
    @Hativ You should not use [nested properties](https://stackoverflow.com/questions/15884096/organize-prototype-javascript-while-perserving-object-reference-and-inheritance). If you want "subclass" prefixes, just use `.foo_getFoo()`, `.foo_connect()`, `.bar_getBar()` etc. If you insist on the properties, that comes down to the "Composition" approach. Of course you can write a helper function that allows to compose multiple parts into one large class for that as well. – Bergi Aug 23 '17 at 21:51
  • Thank you, I will go for your "Composition" approach. One last thing: Would it be possible to use `this.output` instead of `this.foo.output`? If I try to change `this.foo = foo;` to `this.output = foo.output;` in the constructor I loose the state. – Hativ Aug 24 '17 at 11:21
  • @Hativ You can store the `output` in the Bar component if you want and that fits your use case, but don't store it in both instances. You would then have a Foo with `constructor() { this.bar = new …Bar(1); }` and `changeOutput(o) { this.bar.output = o; }`. – Bergi Aug 24 '17 at 20:10
0
class Foo {
  constructor() {
    this.output = 1;
  }

  changeOutput(output) {
    this.output = output;
  }
}

class Bar extends Foo {
  getOutput() {
    return this.output;
  }
}

const foo = new Foo();
foo.changeOutput(2);

const bar = new Bar(); 
bar.changeOutput(2);

console.log(bar.getOutput()); //

When you're creating an a child instance, Bar will call Foo's constructor and initialize output's value to be 1. When you're call the getOutput method, it will change bar's output value.

A K
  • 1,464
  • 2
  • 12
  • 18
  • I want to share `this.output` between `Foo` and `Bar`, not set it twice. A second instance should not be created. `Foo` and `Bar` should share the same instance. – Hativ Aug 22 '17 at 21:08