1

I’ve got a quick javascript question.

Say I’ve got RootFile.js

import UserApi from './UserApi'
export default class RootFile {

  get userApi() {
    return UserApi;
  }
};

and then I got UserApi.js

import Auth from './auth';
import Profile from './profile';

const merged = {
  ...new Auth, 
  ...new Profile 
}

export default merged;

and then I got separate functionality files likeauth.js or profile.js.

auth.js

export default class Auth{
  authLog(){
    console.log("DONE");
    //Gotta find a way to run this.
  }
}

profile.js

export default class Profile{
  profileLog(){
    console.log("DONE");
    //Gotta find a way to run this.
  }
}

Now I want to be able to call:

import RootFile from './RootFile'

RootFile.userApi.profileLog();
//and
RootFile.userApi.authLog();

I can't get that to work, RootFile.userApi is a typeof object, but authLog is undefined. What am I doing wrong?

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
SudoPlz
  • 20,996
  • 12
  • 82
  • 123
  • `profileLog` and `authLog` are *inherited* methods and won't be copied by `Object.assign` or the property spread operator. However, you actually don't really *want* to combine multiple classes that have nothing to do with each other in one object. If they are supposed to be composable, make them mixins. – Bergi Apr 07 '16 at 15:45
  • @Bergi could you please write an answer to explain the right way to do this? – SudoPlz Apr 07 '16 at 16:20
  • I'm not even sure why you are doing this, so I don't know what the "right" way is. Are these things supposed to be singletons? How would you have written them without `class`? – Bergi Apr 07 '16 at 16:22
  • You see perhaps they don't have to be classes, what I really want to achieve is, have a set of functions that have to do with the profile, another set of functions that have to do with auth, another set of functions that have to do with something else etc.. What I wish to achieve ultimately is be able to reference all those functions from my `UserApi`, in a clean way, without having to write separate getters for each one of the functions. Coming from C++, Java and Objective-C, javascript always seemed weird to me without classes, so perhaps thats why I'm confused. – SudoPlz Apr 07 '16 at 16:25

4 Answers4

4

What I ended up doing after all was the following:

My RootFile.js looks like this now:

import UserApi from './UserApi'
export default class RootFile {

    get userApi(){
        return UserApi;
    }
};

I got rid of the get because @Tim said they are not that performant.

then my UserApi.js looks like this now:

import * as Auth from './auth';
import * as Profile from './profile';

const merged = {
  ...Auth, 
  ...Profile 
}

export default merged;

no more new.

and then I got separate functionality files likeauth.js or profile.js.

auth.js

export function authLog(){
    console.log("auth test ");
},
export default auth;

profile.js

export function profileLog(){
    console.log("profile test ");
} 
export default profile;

So no more classes, as @Bergi suggested.

Now I am able to call:

import RootFile from './RootFile'

RootFile.userApi.profileLog();
//and
RootFile.userApi.authLog();

Thank you all for your answers, but that is how I'll do it after all, it works great.

SudoPlz
  • 20,996
  • 12
  • 82
  • 123
  • 1
    Even better: Instead of default-exporting objects, you should use named exports. You still can `import * as Auth from './auth';` – Bergi Apr 07 '16 at 17:13
  • 1
    @Bergi just changed my answer to reflect that :) Thanks. – SudoPlz Apr 07 '16 at 17:35
  • 1
    I've simplified it even a bit more :-) I'm not sure whether you really need two distinct files `RootFile` and `UserApi` at all, currently it looks like you should merge them into one. – Bergi Apr 07 '16 at 17:41
  • Thanks @Bergi, but you see I want to be running code in RootFile constructor. I want to pass the token and save it in a property within it. Also there will be more than just the userApi so the root file will have to include all of the other apis. I want to be able to run `RootFile(token).userApi.whateverFunction();` and `RootFile(token).whateverApi.whateverFunction();` – SudoPlz Apr 08 '16 at 10:55
  • OK, sure, then make `RootFile` a constructor/factory; you just had never used it that way in your question or answer before. Though it still seems weird to me; why would an instantiable object have static `…Api` properties? – Bergi Apr 08 '16 at 11:02
  • @Bergi in order to not have to reenter the token on each api call that needs it. If its there every call will be using it. Does this make sense or is my approach wrong? – SudoPlz Apr 08 '16 at 11:06
  • Nah, I can't make sense of that. How would a statically defined api function use the token from the dynamic `RootFile` instance? Maybe you want to make the token a static property as well? – Bergi Apr 08 '16 at 11:10
  • @Bergi Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/108622/discussion-between-sudoplz-and-bergi). – SudoPlz Apr 08 '16 at 11:11
1

I don't think that use of the ... spread operator is correct. Try using Object.assign instead - it takes a target object and assigns all of the enumerable properties of other objects to it.

import Auth from './auth';
import Profile from './profile';

let merged = {};
Object.assign(merged, new Auth, new Profile);

export default merged;
Jack Guy
  • 8,346
  • 8
  • 55
  • 86
  • I saw the `...` spread operator use for that in this answer of SO http://stackoverflow.com/a/32372430/1658268 – SudoPlz Apr 07 '16 at 15:30
  • I'll give it a try and let you know in a few minutes..! – SudoPlz Apr 07 '16 at 15:34
  • @SudoPlz: The newest version of Babel (v6) no longer includes features from stage 2 and up proposals by default - the answer you linked is from before that version was released, I think. You now have to include the [stage 2 preset](https://babeljs.io/docs/plugins/preset-stage-2/) in your `.babelrc` configuration (as well as the standard ES2015 one) to get that behavior. – Joe Clay Apr 07 '16 at 15:39
  • It's true that the spread operator is not ES6 (and won't be in ES7 either), but `Object.assign` doesn't do anything different, it still won't work. – Bergi Apr 07 '16 at 15:48
0

I do not think you want to do that. The whole point of separating the logic in their respective classes is getting a more structured and better maintainable library.

I would go with composition:

export default class RootFile  {

  get userApi() {
    // Some logic here?

    // Just return a newly created api for now:
    return new UserApi;
  }
};

Do the same with UserApi:

export default class UserApi {

  get profile() {
    return new Profile;
  }
};

And use it like this:

rootFile.userApi.profile.log("etc");

Why composition?

  • This way you do not have to worry about redefinitions of functions.
  • It is faster, JavaScript engines can now optimize for your classes while this is not possible for merged constructs.

Also remember that getters are less performing than properties. I think you should consider using properties for common used members of classes

Community
  • 1
  • 1
Tim
  • 5,521
  • 8
  • 36
  • 69
  • There will be lots and lots of functions and I really want to spread them in separate files.. Also since they will all be part of the UserApi (and there will be other types of APIs I want to avoid using a separate name for each functionality. – SudoPlz Apr 07 '16 at 15:32
  • 1
    I understand, you can use this pattern as many times as you like to nest APIs (see edit). This way you do not have to worry about redefinitions of functions. Also remember that getter are less performing than properties. I think you should consider using properties for common used members of classes. – Tim Apr 07 '16 at 15:34
  • Thanks for the time you took to answer this question, but I really want to call `rootFile.userApi.profileLog()` and not separate it to `profile.log()` Also about the getter being less performant, I will dump them if thats the case I didn't know that, thank you! – SudoPlz Apr 07 '16 at 15:38
  • Alright. Maybe if you can tell me why you want it this way I can be of further help. – Tim Apr 07 '16 at 15:40
  • @SudoPlz: If you just want to spread out one big class over multiple files, you should make the files export the methods only and not a whole own class. – Bergi Apr 07 '16 at 15:50
  • @Bergi that seems to be what I want to do, but I don't want to write tons of boilerplate code to call each function separately on `UserApi`, I want all the functions I write in `auth.js` and `profile.js` to be ready to be used without writing separate getters for each one of them in my `UserApi`. – SudoPlz Apr 07 '16 at 15:53
  • @SudoPlz: Try to write your modules without using the `class` keyword - I guess that'll be enlightening :-) – Bergi Apr 07 '16 at 16:25
  • @Bergi You mean export pure functions? – SudoPlz Apr 07 '16 at 16:29
0

I did this -

import { One } from './one.js'
import { Two } from './two.js'
import { Three } from './three.js'

const MasterClazz2 = {
    ...new One(),
    ...new Two(),
    ...new Three()
}

export default MasterClazz2

Then I import like this-

import func from './combinedClazz.js'

func.oneFunc()
func.threeFunc()
func.threeFunc()
func.threeNameFunc('Sebastian')

console.log('variable: ' + func.one)
console.log('variable: ' + func.two)
console.log('variable: ' + func.three)

func. shows all the variables and functions from classes One, Two and Three in the intellisense as if they were from one class

Sebastian
  • 1
  • 2