Question

Looked around SO and didn't find anything that seemed to match what I am trying to do..

I am trying to reference an object by a string representation, though everywhere I look I see that using eval() is bad - though can't find a way to do this without using eval()

So my use case:

I have a data attribute on a button;

data-original-data-object="window.app.myData.originalData"

When the button is clicked I need to access the actual object held at window.app.myData.originalData

Now, I know I can do:

var dataObj = eval($(this).data('original-data-object'));

Though is there any other way to do this?

If it helps, the data that is stored at window.app.myData.originalData is a JSON object.

Was it helpful?

Solution 2

A couple of solutions come to mind. The first solution is hinted at in @CD..'s answer. The second is to restrict that string via a regex to just property names so you can safely use eval.

Traversing the window object to get the value (no eval)

function getValue(s) {
    var keys = s.split("."), o = window, key, i, length, undef;

    if (keys[0] === "window") {
        keys.shift();
    }

    for (i = 0, length = keys.length; i < length; i++) {
        key = keys[i];

        if (!(key in o) || o[key] === null || o[key] === undef) {
            throw new Error("Could not get value of " + s);
        }

        o = o[key];
    }

    return o;
}

Restricting the string to valid property names:

function getValue(s) {
    var regex = /^[\w$][\w.]+$/, value;

    if (regex.test(s)) {
        try {
            value = eval(s);
        }
        catch (error) {
            throw new Error("Could not get value of " + s + " (" + error.message + ")");
        }
    }
    else {
        throw new Error("Could not get value of " + s);
    }

    return value;
}

To use:

var x = getValue(this.getAttribute("data-original-data-object"));

You want to avoid using eval because it can arbitrarily execute JavaScript that you may or may not have control of. In this particular case, you know the exact kind of string you want. In my opinion, I'd use a regular expression to make sure the string just contains property names separated by dots. Security speaking, there is no difference between these two lines of code:

var x = eval("window.foo");
var x = window.foo;

OTHER TIPS

Like this:

var obj = (function(str){
  var arr = str.split('.');

  if (arr[0] === 'window'){
    arr.shift();
  }

  return arr.reduce(function(a, b){
     return a[b];
  }, window);

}("window.app.myData.originalData"))

Provided that you can ensure that the attribute cannot be modified in anyway that can cause harm to the site/project that this is being implemented on, I don't see any problems.

I'm not sure if this will work for your situation, but a simple solution that avoids eval may be to add "window.app.myData.originalData" with its JSON data as the property of an object that will remain in scope.

Something like:

var exampleData = { id:1, content:"..." };

var dataStore = { "window.app.myData.originalData": exampleData  };

Then, in your click handler:

var retrievedData = dataStore[$(this).data('original-data-object')];    // uses "window.app.myData.originalData" to retrieve exampleData

In this case, you will need to access the data using bracket notation because of the . character in the property name. This approach should be faster and safer than trying to use eval, however.

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