dt1000
dt1000

Reputation: 3732

Call function on object literal, represented by string - JavaScript

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.

Upvotes: 2

Views: 571

Answers (1)

Jeremy Banks
Jeremy Banks

Reputation: 129756

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();

Upvotes: 1

Related Questions