2

I am using history.js. In the stateObj of the pushState function, I want to add a reference to a function (Car.init(), or Boat.init(). In C++, I believe I could use a function pointer.

Then on window.onpopstate, I want to reference that function and call it. I can read the string (Car.init(), but then how can I call the function? I don't want to use eval.

7
  • don't quote, and don't invoke the functions. Commented Feb 26, 2016 at 0:21
  • @JeremyBanks: Of course we don't call them "pointers" in JavaScript, we call them function objects :-) Commented Feb 26, 2016 at 0:27
  • you should be able to call the functions you pass to pushState(). if not, then put the functions on a global object and pass the property name to pushState, using the key to find the function in onpopstate() Commented Feb 26, 2016 at 0:30
  • nobody complains about eval() when $.html() or $.load() use it... it can also be used to increase performance in many applications by dynamically generating a perfect-shaped piece of logic for the task. Commented Feb 26, 2016 at 0:38
  • i understand that/why you don't like it, but what makes you sure it will be gone one day? there are other sides you might not be considering; mustache.js slowed down a bunch when it got rid of dynamic code so that it could run in chrome extensions and tight CSPs... Commented Feb 26, 2016 at 0:42

1 Answer 1

1

You probably shouldn't, but if you do want to invoke a function based on a global dotted-path name, that could be accomplished like this:

function callFunction(name, var_args) {
  // break the string into individual property/method names
  var parts = name.split('.');

  // start with a reference to the global object
  var target = window;
  var previousTarget = null;

  for (var i = 0; i < parts.length; i++) {
    // keep a copy of the previous reference to use as the `this` value
    previousTarget = target;

    // change the reference to the next named property
    target = target[parts[i]];
  }

  // grab the remaining arguments
  var args = Array.prototype.slice.call(arguments, 1);

  // call the target function, with previousTarget as the subject, using args
  return target.apply(previousTarget, args);
}

// This is in the top-level/global scope. This won't work for a local definition.
var MyApp = {
  currentUser: {
    name: 'Joe',
    displayName: function(greeting) {
      alert(greeting + " ," + this.name + "!");
    }
  },
  openBar: function() {
    alert("The Foo Bar is now open for business!");
  }
};

var functionName = 'MyApp.currentUser.displayName';
callFunction(functionName, "Hello");

This is safer than using eval (good call on avoiding it), but is still pretty wacky and very difficult for JavaScript interpreters to optimize. Instead, the recommended approach is to use a reference (pointer) to the function. This is probably similar to what you'd do in C++. If the function doesn't use this (i.e. if it's a static function, not a method), you can just take a reference to the function directly.

var open = MyApp.openBar;
open();

If it does have a this value, you'll need to use the .bind() method to preserve its association with the object it's attached to.

var display = MyApp.currentUser.displayName.bind(MyApp.currentUser);
display("Greetings");

If you pass additional arguments to .bind(), you can also specify the leading arguments that will be used to call the function.

var displayHello = MyApp.currentUser.displayName.bind(MyApp.currentUser, "Hello");
displayHello();
Sign up to request clarification or add additional context in comments.

3 Comments

It is a static function in an object literal, such as: var MyGreatObj = { myStaticFunction: function myStaticFunction(foo){ //do something}}. So how can I just 'take a reference to the function directly', as you say? Thanks!
var reference = MyGreatObject.myStaticFunction;, so you can later reference(bar). You identify it the same way was you would to call it, but drop the (argument list).
BTW, for anyone checking, this solution reminds one how to send a reference of a function. But the pushState function of history.js will never handle an object that can't be serialized. So I made a map that defines a function to be associated with a string. That way, you can push a string onto the pushState, and then reference the function when you get the string back.

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.