0

Let's say I have an object that looks like this:

{
     'apple': 'nice',
     'banana': 'decent',
     'cherry': 'yuck',
}

and I have these two methods:

function eatItems(cherry, apple) { }
function throwItem(banana) { }

My two questions:

  1. Is it possible for me to invoke eatItem and send the arguments in the correct order? Maybe something like:

    eatItems.call(this, {'cherry': cherry, 'apple': apple});

  2. What if I don't know what arguments eatItems receives, can I dynamically look up the names of the arguments for a function so I can know the order that I need to throw them in?

4
  • 1. Objects have no order. 2. There is arguments. Commented Aug 8, 2014 at 23:11
  • 1
    What is the problem you are trying to solve? What you're asking sounds odd, and that may mean that you're approaching a problem from the wrong angle. Commented Aug 8, 2014 at 23:17
  • Adding to what pointy said, most of the time whoever writes a function is free to rename the arguments as they wish. I am not a big fan of making the argument names be significant, like they do in angularjs. Commented Aug 8, 2014 at 23:27
  • 1
    Look what I just created earlier today: jsbin.com/xatavoga/1/edit?html,js,output Commented Aug 8, 2014 at 23:27

2 Answers 2

2

There's a way, indeed, and it involves calling toString on a function:

var source = eatItems.toString();
// => "function eatItems(cherry, apple) { }"

The next step is to parse the string you've got to get the names of the arguments:

var args = source.substring(source.indexOf("(") + 1, source.indexOf(")")),
    argNames = /\S/.test(args) ? args.split(/\s*,\s*/) : [];

A few caveats:

  1. This solution has been kept quite simple. It doesn't handle comments in the function definition.
  2. Not every browser can correctly convert a function to a string (the PS3 browser comes to my mind), but they're a really small minority anyway.
  3. I haven't tested it, but there may be some performance issues on slower machines and/or older browsers with large functions.

And, overall, this solution is more like an exercise. I wouldn't recommend taking this pattern in Javascript. Don't forget that some functions handle a variable number of arguments, and you won't find them listed in their definition. Rethink your code, and find a better way.

Sign up to request clarification or add additional context in comments.

1 Comment

This is true, but it should be noted that a JavaScript system architecture that relies on having to do this is a pretty weird beast.
1

If I understand correctly you want extract the argument names from the function, and inject data from an object based on those names. This can be accomplished by converting the function to a string, extracting the arguments, and applying the function with those arguments:

function inject(data, f) {
  var args = f.toString()
    .match(/function\s*?\((.+?)\)/)
    .pop()
    .split(',')
    .map(function(a){return data[a.trim()]})
  return function() {
    return f.apply(this, args)
  }
}

var data = {
  apple: 'nice',
  banana: 'decent',
  cherry: 'yuck',
}

var eat = inject(data, function(cherry, apple) {
  console.log(cherry, apple)
})

eat() //=> yuck, nice

The obvious problem with this approach is that it is highly dependent on the variable names, so when you minify your code, the variables will get mangled and the function will stop working. This is a known problem in AngularJS, which uses something similar for their dependency injection.

This is often an XY problem, or an anti-pattern at the very least.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.