1

I have a function that currently returns an object containing subfunctions.

let myFunction = (data) => {
  return {
    a: function(){
      return a()
    },
    b: function(){
      return b()
    }
  }
}

let a = () => {
  return 'a'
}

let b = () => {
  return 'b'
}

console.log(myFunction().a())
//=> 'a'

console.log(myFunction())

It works perfectly. However, when I run myFunction(), it returns { a: [Function: a], b: [Function: b] }. I would like myFunction() to return another function, alphabet, which returns a string of the alphabet.

Example (obviously not working):

let myFunction = (data) => {
  // CHANGED BELOW
  return alphabet() && {
    a: function(){
      return a()
    },
    b: function(){
      return b()
    }
  }
}

let a = () => {
  return 'a'
}

let b = () => {
  return 'b'
}

let alphabet = () => {
  return 'abcdefghijklmnopqrstuvwxyz'
}

console.log(myFunction().a())
//=> 'a'

console.log(myFunction())
//=> {a: [function a], b: [function b]}

How can I get the function to default to returning one function, while also having the sub-functions available?

Help appreciated. Thanks

imaginate
  • 567
  • 2
  • 13
  • I'm struggling to understand the end result you're looking for. In particular, your second code snippet with what you want (although not working as you said) shows `{a: [function a], b: [function b]}` as the desired(?) output of calling `myFunction()`...? – T.J. Crowder Jan 13 '21 at 18:53

2 Answers2

2

I think you're saying that you want myFunction to return a function that returns the alphabet, but which also has a and b functions on it.

You can do that, since functions are objects, and objects can have arbitrary properties. There's no literal syntax for functions plus properties, but you can easily add them via assignment or with Object.assign:

let myFunction = (data) => Object.assign(
    () => "abcdefghijklmnopqrstuvwxyz",
    {a, b}
);
    
let a = () => {
    return "a";
};

let b = () => {
    return "b";
};

console.log(myFunction().a());
//=> "a"

console.log(myFunction()());
//                      ^^−−−−−−−−−−− Note the extra ()
//=> "abcdefghijklmnopqrstuvwxyz"

For clarity, here's a more verbose version of myFunction:

let myFunction = (data) => {
    const alphabet = () => "abcdefghijklmnopqrstuvwxyz";
    alphabet.a = a;
    alphabet.b = b;
    return alphabet;
);

The original above does the same thing (other than that this more verbose version gives alphabet a name), but this version may be easier to read.

Or you can have the name like this, too:

let myFunction = (data) => {
    const alphabet = () => "abcdefghijklmnopqrstuvwxyz";
    return Object.assign(alphabet, {a, b});
);

In a comment you've said:

I would like to call myFunction() without the extra ()'s and get the full alphabet

That's basically not possible. You can get close, but you can't literally get there.

The "close" you can get to is that you can return a String object rather than a string primitive:

let myFunction = (data) => Object.assign(
    new String("abcdefghijklmnopqrstuvwxyz"),
    {a, b}
);
    
let a = () => {
    return "a";
};

let b = () => {
    return "b";
};

console.log(myFunction().a());
//=> "a"

console.log(myFunction());
//=> "abcdefghijklmnopqrstuvwxyz"

String objects are objects, not just string primitives, which means they can have additional properties, like a and b. But any time they're coerced to a primitive form, they go back to being a string primitive. For instance, if you use + on them. Also, String objects have all the String methods (they get them from the same place string primitives do), and the ones that return strings return string primitives, so calling (for instance) toLowerCase on the result also gives you a string primitive. Here's an example of both + and toLowerCase():

let myFunction = (data) => Object.assign(
    new String("abcdefghijklmnopqrstuvwxyz"),
    {a, b}
);
    
let a = () => {
    return "a";
};

let b = () => {
    return "b";
};

console.log(myFunction().a());
//=> "a"

console.log("alphabet: " + myFunction());
//=> "abcdefghijklmnopqrstuvwxyz"

console.log(myFunction().toLowerCase());
//=> "abcdefghijklmnopqrstuvwxyz"

I do not recommend doing this. Again, String objects are objects, not the usual kind of strings, and that will likely cause some trouble in your program at some point.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • I think the OP is asking for (and you're supplying) one more level of indirection than is needed. I think the OP might wish to pass a parameter to myFunction and have it return any of those letter-producing functions (not an object) as result. The default -- no parameter -- would result in the whole-alphabet function. – danh Jan 13 '21 at 18:55
  • @danh - Yeah, it's really hard to say. – T.J. Crowder Jan 13 '21 at 18:56
  • @T.J.Crowder, Thanks for answering! I think this is as close as I can get to my solution. I would prefer if I could return the value and not call the extra `()` (e.g. myFunction() instead of myFunction()(). Is this possible? Thanks – imaginate Jan 13 '21 at 18:58
  • @danh yes, that is what I would like. When you specify the sub-function you want, you get the letter back (`myFunction().a()` => `'a'`) but when you call `myFunction()` you get `abcdefghijklmnopqrstuvwxyz` back (`myFunction()` => `'abcdefghijklmnopqrstuvwxyz'` – imaginate Jan 13 '21 at 19:00
  • Perhaps as I've answered, @imaginate? Though this post by TJCrowder answer is a perfectly sensible interpretation of the question as well. – danh Jan 13 '21 at 19:05
  • @danh this answer is good for this problem because chaining something like that is not possible as i understand https://stackoverflow.com/a/12679851/13647574 – MUHAMMAD ILYAS Jan 13 '21 at 19:05
  • @MUHAMMADILYAS I don't think that solves my problem (?) because I don't need to tell if a function has been chained, only returning an object if a subfunction is not called. – imaginate Jan 13 '21 at 19:09
  • @imaginate yes because that is not possible (as I think), because myFunction() will never know there is a successor ( or a chained property) – MUHAMMAD ILYAS Jan 13 '21 at 19:11
  • @imaginate - *"I don't need to tell if a function has been chained, only returning an object if a subfunction is not called"* I'm afraid I still don't understand. If you want an object, your original code does that. But you've said you want it to return a function, so I'm really not sure what you're looking for. Do you want `myFunction()` (without the extra `()`) to give you `"abcd...."`? – T.J. Crowder Jan 13 '21 at 19:14
  • @T.J.Crowder Exactly! I would like to call myFunction() without the extra ()'s and get the full alphabet. I am not sure if that's possible, and if it isn't, i'll mark your answer as the solution. Thanks so much :) – imaginate Jan 13 '21 at 19:18
  • Not possible, if I now understand correctly @imaginate. Without a param, myFunction doesn't and can't know if you want a function or an object from it. I elaborated in an edit to my answer. – danh Jan 13 '21 at 19:19
  • @danh ok, i will look. Thanks so much! – imaginate Jan 13 '21 at 19:21
  • @imaginate - I've updated the end of the answer. It's possible to do something close to that, but I don't recommend it. :-) Happy coding! – T.J. Crowder Jan 14 '21 at 07:33
  • @T.J.Crowder ABSOLUTELY AMAZING!!! Thanks so so so much! did exactly what I needed. Thanks again!!!!!!!!!! – imaginate Jan 14 '21 at 17:11
  • @T.J.Crowder before I wrote this question I knew I had seen this being used before, which is why I asked this question. Now, when I went to make a get request, I realized where I had seen it. [Simple-get](https://github.com/feross/simple-get/blob/81eaf56c012dec4f64f223f4c41811e32ea2ce53/index.js#L95), the library that I use to fetch in Node.js. Could you take a look at this library and add the same thing but returning a function (like simple-get does), or is this already done? Thanks – imaginate Jan 14 '21 at 17:18
  • @imaginate - Looks to me like Simple-get just provides a function with properties on it, like the first example above. jQuery is another famous example of that. – T.J. Crowder Jan 14 '21 at 18:11
  • @T.J.Crowder Sorry for not responding for ages, I've been pretty busy finishing some applications for school. I actually have seemingly broke what you said and found a glitch (based off of what you said) that does exactly what I need. Is this valid? Here is the code: `let myFunction = (data) => { return Object.assign("abcdefghijklmnopqrstuvwxyz", {a, b}) };` – imaginate Jan 25 '21 at 01:25
  • @T.J.Crowder Here is a working fiddle: https://jsfiddle.net/zLbgvmnf/ – imaginate Jan 25 '21 at 01:27
  • (or view the easy example): https://jsfiddle.net/zLbgvmnf/2/ – imaginate Jan 25 '21 at 04:34
  • @imaginate - That's the second Stack Snippet in the answer, just using implicit primitive-to-object conversion rather than explicit object creation. `Object.assign` converts its first argument to an object, so `Object.assign("some string", ...)` and `Object.assign(new String("some string"), ...)` have the same result. – T.J. Crowder Jan 25 '21 at 08:06
  • @T.J.Crowder I didn't realize that. Thanks so much!!!!!! – imaginate Jan 25 '21 at 16:43
1

EDIT

I think I understand now, reading your comment, but I worry you are asking for something impossible. In a way, you're saying: "I want my function to return an object if the code that follows the invocation dereferences that object". Broken into syntax equivalent lines, you're saying...

let iWantAnObject = myFunction()
iWantAnObject.a() // => 'a'  it works!

let iWantAFunction = myFunction() // same invocation of myFunction, here, it needs to know about the next line of code
iWantAFunction() // oh no, I can't invoke an object :-(

The only idea I have is to pass a param to myFunction to let it know what you want, to let it know what you plan to do with what it returns.

original...

An alternative idea is to return a function, not an object, selected by the parameter...

let myFunction = (data) => {
  const letterFunctions = {
    a: function(){
      return 'a'
    },
    b: function(){
      return 'b'
    }
  }
  const allLettersFn = () => 'abcdefg...'
  return data ? letterFunctions[data] : allLettersFn;
}

let fnB = myFunction('b')
let fnAll = myFunction()

console.log(fnB)
console.log(fnB())

console.log(fnAll)
console.log(fnAll())
danh
  • 62,181
  • 10
  • 95
  • 136
  • Thanks for responding! Sorry if I was unclear, I don't want to return values based upon the `data` parameter, the `data` parameter is unused. For example: `myFunction('a')` doesnt return anything but `myFunction().a()` returns `'a'`. Thanks! – imaginate Jan 13 '21 at 19:09