0

How to check if the string parameter passed in a function is too callable/function but not directly under window..

I know the open/ directly callable function can be checked using the syntax window['functionName']

But how about the member function declared inside an object to be checked?

In below example openFunction() can be called but how to call obj1.foo()?

Prefer not to use eval()

Example Code:

var obj1 = {
  foo: function() {
    alert("I'm a function");
  }
}

function openFunction() {
  alert("I know i am easily callable");
}

function callSomeone(txtcallback) {
  var fn = window[txtcallback];
  if (typeof fn === 'function') {
    fn();
  }
  console.log(typeof fn);
}

callSomeone('openFunction'); //function
callSomeone('obj1.foo'); //undefined
Amit Shah
  • 7,771
  • 5
  • 39
  • 55
  • You should take a look to JsonPath. It uses a kind of `XPath` for "querying" objects => http://goessner.net/articles/JsonPath/ – ADreNaLiNe-DJ Jun 17 '16 at 08:22

3 Answers3

1

try this

var obj1 = {
  foo: function() {
    alert("I'm a function");
  }
}

function openFunction() {
  alert("I know i am easily callable");
}

function callSomeone(txtcallback) {
    str =txtcallback.split(".");
    temp = window;
    for(check in str){
        temp = temp[str[check]];
         if (typeof temp === 'function') {
            temp();
            break;
         }else if(typeof temp === 'undefined'){
             break;
         }
    }
  console.log(typeof temp);
}

callSomeone('openFunction'); //function
callSomeone('obj1.foo'); //function
angel.bonev
  • 2,154
  • 3
  • 20
  • 30
1

If you ant to look for members inside nested maps you have to use a recursive approach.

function callSomeone(txtcallback) {
  var keyPath = txtcallback.split(".");
  var fn = keyPath.reduce(function (member, key) {
    return member[key];
  }, window);

  if (typeof fn === 'function') {
    fn();
  }
  console.log(typeof fn);
}

the downside in this example is that the function is executed in the global scope. If you need to keep the scope of the container object you need to also save the scope.

var obj1 = {
  foo: function() {
    alert("I'm a function");
    return this;
  }
}

function openFunction() {
  alert("I know i am easily callable");
  return this;
}

function callSomeone(txtcallback) {
  var keyPath = txtcallback.split(".");

  var scope = null;

  var context = null;
  var fn = keyPath.reduce(function (member, key) {
    scope = member;
    return member[key];
  }, window);

  if (typeof fn === 'function') {
    context = fn.call(scope);
  }
  console.log(typeof fn, context);
}

callSomeone('openFunction'); //function
callSomeone('obj1.foo'); //undefined
Torsten Walter
  • 5,614
  • 23
  • 26
  • Normally I would use a library such as lodash to accomplish this. This is merely showing how it could work. In lodash I would probably use `_.result`. https://lodash.com/docs#result – Torsten Walter Jun 17 '16 at 10:42
1

It returns undefined because, your code is equivalent to window["obj1.foo"] which is not correct.

The correct way to access to foo function is window["obj1"]["foo"].

So you have to "cycle" through the string obj1.foo.

Here I added a GetProp function that do that cycle and is recursive, so the level of nesting is not a problem.

var obj1 = {
  foo: function() {
    alert("I'm a function");
  }
}

function openFunction() {
  alert("I know i am easily callable");
}

function callSomeone(txtcallback) {
  var fn = GetProp(window, txtcallback.split("."));
  if (typeof fn === 'function') {
    fn();
  }
  console.log(typeof fn);
}

function GetProp(obj, props) {
  if(props.length == 0) {
    return obj;
  } else if(obj[props[0]] != undefined) {
    obj = obj[props[0]];
    return GetProp(obj, props.slice(1));
  }
}

callSomeone('openFunction'); //function
callSomeone('obj1.foo'); //undefined
ADreNaLiNe-DJ
  • 4,787
  • 3
  • 26
  • 35
  • Thanks, i found yours is most professional way of coding solution. – Amit Shah Jun 17 '16 at 10:14
  • i have extended question on this. if i wants to maintain an index (id) variable/member of the object. calling such way it getting lost it's value. `var obj1 = { id: 0, foo: function() { ++this.id; alert("id = " + this.id); } }` – Amit Shah Jun 17 '16 at 11:30
  • 1
    In this case, try JsonPath. It should work for your need. – ADreNaLiNe-DJ Jun 17 '16 at 11:44
  • replacing `this.id` with `this.obj1.id` or `window.obj1.id` works. (what is the difference if you can explain.) but why not the syntax according to JsonPath tutorial works `$.obj.id` works? – Amit Shah Jun 17 '16 at 13:19