0

I am enumerating through the properties of an object. It works fine when I set the object directly.

I need to use prompt (class assignment) to allow the user to input an object name. The problem is obj returns as a string

How can I cast the value from the prompt to an object?

function enumObject(){

    var obj;
    var propertyName;


    obj = prompt("Please enter an object name","window.navigator");
    obj = window.navigator;

    if (typeof obj == "object"){
        for (propertyName in obj){
            document.write(propertyName + " : " + obj[propertyName] + "<br>");
        }
    }
    else {
        alert('The object name is undefined.');
    }

    obj = null;
    propertyName = null;

}

enumObject();
Nick Dickinson-Wilde
  • 1,015
  • 2
  • 15
  • 21
Mel
  • 119
  • 1
  • 9

3 Answers3

1

An object name...?!?

Do you want your user to inform a global variable name which refers an object? If so, you could do:

var name = prompt("Enter a global variable name.");
var obj = window[name];

If you want your user to enter a string which will be converted to an object literal, you could do:

var objDef = prompt("Enter a string representing an object");
var obj = eval("(function(){return " + objDef + ";})()");

or...

var objDef = prompt("Enter a string representing an object");
var obj = new Function("return " + objDef)();

If you want to access a object variable in a function scope and you're not using "strict mode", try:

(function(){
  var someObj = { b:123, c: 321 };
  var name = prompt("Enter an object variable name in local scope.");//Here, your user could enter "someObj"
  eval("console.log(" + name + ")");
})();
Alcides Queiroz
  • 9,456
  • 3
  • 28
  • 43
  • Don't use the window object for this (see comment on Nick's answer). Also, don't execute random code entered by a user. If it's an object literal, use *JSON.parse*, not *eval* or the Function constructor. – RobG Jan 29 '14 at 02:11
  • Sure, JSON.parse is safe since it doesn't depends on evaluating the code in order to parse it. But the question is not enough clear about its intent neither if the entered object is a valid JSON (a JS object can have function properties, this is not truth for a JSON object). About using the window object: I also know that it's a bad practice to give user the power to pollute and change properties in the global namespace. The question asked for something, and I'm trying to solve its problem and giving a reasonable, simple and easy-to-understand answer. – Alcides Queiroz Jan 29 '14 at 02:23
  • 1
    @RobG: There's nothing wrong with executing random code entered by the user who will execute it? – Bergi Jan 29 '14 at 02:35
  • @Bergi—not sure if that's a question or statement. The intention of the OP seems to be to create object properties with user specified names. Using *eval* for that means providing a lot more functionality than just naming properties, e.g. trashing the value of any variable in scope, deleting properties, modifying the DOM. – RobG Jan 29 '14 at 03:09
  • 1
    @RobG: It was a statement :-) As far as I understood, the OP does not want to create object properties but retrieve them - `prompt()` is returning a string with some identifiers which he wants to resolve to a value (object) - is his terms "cast string to object". This answer just seems to hit that problem. – Bergi Jan 29 '14 at 03:26
  • Ok, I think I get it now. *eval* is not required. – RobG Jan 29 '14 at 05:52
0

I would suggest:

function enumObject(){

var obj;
var propertyName;


obj = prompt("Please enter an object name","window.navigator");
window[obj] = {};

if (typeof window[obj] == "object"){
    for (propertyName in obj){
        document.write(propertyName + " : " + obj[propertyName] + "<br>");
    }
}
else {
    alert('The object name is undefined.');
}    
}

enumObject();
Nick Dickinson-Wilde
  • 1,015
  • 2
  • 15
  • 21
  • If you write `window[obj] = {}`, then `typeof window[obj] == "object"` will always be true. – Chuck Jan 29 '14 at 00:55
  • 1
    No, don't do that. There are various global variables whose value you don't want users to randomly modify, so create a separate object and use that. – RobG Jan 29 '14 at 02:08
  • hmm yes I should have mentioned this example code and is not secure (ie VERY unsecure). – Nick Dickinson-Wilde Jan 29 '14 at 02:40
0

I need to use prompt (class assignment) to allow the user to input an object name. The problem is obj returns as a string

How can I cast the value from the prompt to an object?

You can't since Objects don't have names. You can have either variables or object properties whose value is a reference to an object though:

var obj = {};

creates a variable whose name is obj and whose value is a reference to an object.

If you want to dynamically create variables, you can use eval, but it's not recommended. It is very much warned against to use eval to execute random code entered by users since they are completely unaware of the variables that already exist and that you don't want them to mess with.

What you can do is create an object specifically to add properties to, since almost any string value can be used as a property name. So you might do:

function enumObject(){

    // markup and div will be used later
    var markup = '';
    var div;

    // Here's where the property names will be stored
    var obj = {};
    var propertyName = prompt("Please enter an object name", "window.navigator");
    obj[propertyName] = {};


    // This is redundant since an object was just assigned so it will
    // always return true
    if (typeof obj[propertyName] == "object") {

      // Always perform a hasOwnProperty check so you don't get
      // inherited properties, you only want the ones added by the user
      // Also, don't re-used variables, create a new one
      for (var prop in obj) {

        if (obj.hasOwnProperty(prop)) {

          // Don't use document.write after the page has finished loading
          // as it will clear the entire document first before writing
          markup += propertyName + " : " + obj[propertyName] + "<br>";
        }
      }

    // This should never execute since the test should always be true
    } else {
      alert('The object name is undefined.');
    }

    // Write to the document without destroying it
    div = document.createElement('div');
    div.innerHTML = markup;
    document.body.appendChild(div);
}

Edit

Maybe you want to access object properties based on user defined strings. In that case, you can do something like the following. It only uses eval if the root of the property accessor is not window and only strings that appear to be valid identifiers, not random code:

// Assumes dot notation like window.navigator or foo.bar
function getObjectValueByString(s) {

  var identifier, path = [];

  // Very simple identifier re
  var identifierRe = /^[_$a-z][a-z0-9_$]*$/i;
  s = s.split('.');

  // Loop over parts of s
  for (var i=0, iLen=s.length; i<iLen; i++) {
    identifier = s[i];
    path.push(identifier);

    if (identifierRe.test(identifier)) {

      // Get root of accessor
      if (i == 0) {

        if (identifier == 'window') {
        obj = window;

         // Only use eval on what appear to be valid identifiers
         // not arbitrary code
         } else {
          obj = eval(identifier);
        }

     // If not root, get property value
     } else {
       obj = obj[identifier];
     }

     // Stop if obj isn't an object
     if (typeof obj != 'object') {

        // Message if didn't get to end of accessor
        if (i < iLen - 1) {
          return 'Stopped at ' + path.join('.') + ' = ' + obj;
        }            
      }
    } else {
    return identifier + ' is not a valid identifier';
    }
  }

  return path.join('.') + ' = ' + obj;
}

To play with it:

<input onblur="console.log(getObjectValueByString(this.value));">

A much more rigorous regular expression for identifier names that excludes reserved words is in the accepted answer for Valid Characters for JavaScript Variable Names.

If only properties of window are required, life becomes much simpler and eval is not needed at all, so nor is the regular expression to check for valid identifiers:

function getWindowValueByString(s) {

  var identifier, path = [];
  var obj = window;
  s = s.split('.');

  // Loop over parts of s
  for (var i=0, iLen=s.length; i<iLen; i++) {
    identifier = s[i];
    path.push(identifier);

    // Check only using window
    if (i == 0) {
      if (identifier != 'window') {
        return 'Only valid for properties of window';
      }

    } else {
      obj = obj[identifier];
    }

    // Stop if obj isn't an object
    if (typeof obj != 'object') {

      // Message if didn't get to end of accessor
      if (i < iLen - 1) {
        return 'Stopped at ' + path.join('.') + ' = ' + obj;
      }            
    }
  }

  return path.join('.') + ' = ' + obj;
}
Community
  • 1
  • 1
RobG
  • 142,382
  • 31
  • 172
  • 209