Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to call a method from an object using a string?

Is it possible to call a method from an object using a string?

var elem = $('#test');             //<div id="test"></div>
var str = "attr('id')";  

//This is what I'm trying to achieve
  elem.attr('id');                 //test

//What I've tried so far  
  elem.str;                        //undefined
  elem.str();                      //Object [object Object] has no method 'str'
  var fn = eval(str);              //attr is not defined
  eval(elem.toString()+'.'+str);   //Unexpected identifier

//Only solution I've found so far, 
//but is not an option for me 
//because this code is in a function 
//so the element and method call
//get passed in and I wouldn't know
//what they are
  eval($('#test').attr('id'));     //test
like image 895
Aust Avatar asked Aug 28 '12 19:08

Aust


2 Answers

UPDATE

This is my final, working answer:
After running this code in the console

theMethod = 'attr("id","foo")'.match(/^([^(]+)\(([^)]*)\)/);
jQuery('#post-form')[theMethod[1]].apply(jQuery('#post-form'),JSON.parse('['+theMethod[2]+']'));

The post-form element now has a new ID, no problems at all. This works for methods that take multiple arguments, a single argument or no arguments at all. Recap:

theMethod = theInString.match(/^\.?([^(]+)\(([^)]*)\)/);
//added \.? to trim leading dot
//made match in between brackets non-greedy
//dropped the $ flag at the end, to avoid issues with trailing white-space after )
elem[theMethod[1]].apply(elem,JSON.parse('['+theMethod+']'));

That's the safest, most reliable approach I can think of, really


What ever you do DON'T USE EVAL:

var theMethod = 'attr(\'id\')';
//break it down:
theMethod = theMethod.match(/^([^(]+)\(.*?([^)'"]+).*\)$/);
//returns ["attr('id')", "attr", "id"]
elem[theMethod[1]](theMethod[2]);//calls the method

It's the same basic principle as you'd use with any objects (remember that functions are objects all on their own in JS - and jQuery objects are, well, objects, too). This means that methods can be accessed in the exact same way as properties can:

$('#foo').attr('id') === $('#foo')['attr']('id');

So just break the string apart, and use the method name like you would an object property and you're all set to go.

Just remember: When all you have is the eval hammer, everything looks like your thumb.
Brendan Eich


If there is a chance of multiple arguments being passed to whatever method, you can sort of work your way around that, too (I think - well: logic dictates, but it's rather late and logic is getting beat up by Gin pretty bad now):

theMethod = theMethod.match(/^([^(]+)\(([^)]+)\)$/);
//["attr('id','foo')", "attr", "'id','foo'"] --> regex must now match quotes, too
elem.theMethod[1].apply(elem,JSON.parse('['+theMethod[2]+']'));

This applies the method of whatever element/object you're dealing with to itself, thus not changing the caller context (this will still point to the object within the method) and it passes an array of arguments that will be passed to the called method.

like image 114
Elias Van Ootegem Avatar answered Sep 19 '22 14:09

Elias Van Ootegem


You should use one of these methods:

  • apply

    var result = function.apply(thisArg[, argsArray]);

  • call

    var result = fun.call(thisArg[, arg1[, arg2[, ...]]]);

Here is the sample:

var Sample = function() {
var that = this;

this.sampleMethod = function() {
    return alert("Hello!");
};

this.sampleMethod2 = function(){

    that["sampleMethod"].apply(that);
};  
};

var objImpl = new Sample();

objImpl.sampleMethod2(); //you will get a message from 'sampleMethod()'
like image 22
Sergii Avatar answered Sep 19 '22 14:09

Sergii