Question

I have a JavaScript function what is dig through on object and make a string value to function object. Have this JSON:

{
  "active": true,
  "icons": {
     "activeHeader": "ui-icon-alert"
  },
  "animate": {
     "duration": 1000, "always": "dMethod"
  }
}

I use JSON.parse on this string so I reach options.animate.always as a string with value dMethdod which is actually a name of the method. So I can access this through window[options.animate.always] and I wish to change the options.animate.always from string to method that is pointed to the string. I make a function for this job:

function SetFunctions(options, functionName) {
  var path = functionName.split(".");
  var setterObject = options;
  for (var k = 0; k < path.length; k++) {
    if (setterObject != undefined) {
      setterObject = setterObject[path[k]];
    } else {
      break;
    }
  }
  if (setterObject != undefined && window[setterObject] != undefined) {
    setterObject = window[setterObject];
  }
}

I call this function with the variable returned from the parse and function name animate.always as value. The part that find the correct property is worked, but when I set the value of the setterObject the change is not affect the original value.
I'm thinking to build up the reference as string 'options.animate.always = dMethod' and use eval on it, but I really want to avoid using eval function (I know eval is evil :)).

FINAL SOULUTION:
I put answers together and finished my method. Finally become two methods. I comment it and share maybe useful to others:

function ChangeStringToFunction(functionPath, rootObject, separator) {
  // functionPath is required parameter
  if (functionPath === undefined || functionPath === null) return;
  // rootObject is optional. If not supplied the window object will be the base of the search
  var localRootObject = rootObject === undefined ? window : rootObject;
  // separator is optional. If not supplied the '.' will be the separator
  var localSeparator = separator === undefined ? "." : separator;
  // split the string reference (example "jui.someObj1.someOjb2"
  var pathParts = functionPath.split(localSeparator);
  var currentObject = localRootObject;
  // exclude the last part
  for (var i = 0; i < pathParts.length - 1; i++) {
    currentObject = currentObject[pathParts[i]];
    // it's useless to go forward if there is no object
    if (currentObject === undefined) return;
  }
  // get the string represent the name of the function (full path could be included)
  var currentValue = currentObject[pathParts[pathParts.length - 1]];
  // the value must be a string
  if (typeof currentValue !== "string") return;

  // get the function reference based on the value provided
  var functionReference = ResolveFunction(currentValue);
  // if the result is not a function it's meaningless to continue
  if (typeof functionReference !== "function") return;

  // and finally change the string value of the object with the function value represent by our string
  currentObject[pathParts[pathParts.length - 1]] = functionReference;
}

function ResolveFunction(functionPath, separator, rootObject) {
  if (functionPath === undefined || functionPath === null) return undefined;
  var localRootObject = rootObject === undefined ? window : rootObject;
  var localSeparator = separator === undefined ? "." : separator;
  var pathParts = functionPath.split(localSeparator);
  var currentObject = localRootObject;
  for (var i = 0; i < pathParts.length; i++) {
    currentObject = currentObject[pathParts[i]];
    if (currentObject === undefined) break;
  }

  return typeof currentObject === "function" ? currentObject : undefined;
}
Was it helpful?

Solution

but when I set the value of the setterObject the change is not affect the original value.

Yes, you are only assigning to a variable. That will never change anything else but the variable, since JavaScript does not have pointers.

To change an object, you will have to assign to a property. In your case, you will have to omit the last iteration to get the object which you then assign to:

function SetFunctions(options, functionName) {
    var path = functionName.split("."),
        setterObject = options;
    for (var k=0; setterObject!=null && k<path.length-1; k++) {
        setterObject = setterObject[path[k]];
    }
    var prop = path[k],
        fn = setterObject!=null && window[setterObject[prop]];
    if (fn) {
        setterObject[prop] = fn;
    }
}

Btw, I think in your case it might be easier to build a CallFunctions function that directly invokes the function with the name stored in that property, instead of replacing the property value with the method - unless you plan to invoke it very often.

OTHER TIPS

It depends on the level of indirection you want.

If the method will always be called "always", you can do something like this:

function SetFunction(object, propertyName,  functionName) {
    var functionObj = window[functionName];
    object[propertyName] = functionObj;
}

And call it like this:

SetFunction(myObj.animate, "always", myObj.animate.always);

But I suspect you want something a bit more generic?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top