2011-02-13 20 views
13

Me encontré con una situación en la que necesito acceder a un objeto javascript desde el servidor. El servidor devuelve el nombre de la cadena de la función u objeto y, en función de otros metadatos, evaluaré el objeto de forma diferente.Acceder al objeto javascript espaciado por nombres de cadena sin usar eval

Originalmente estaba evaluando (eval([string])) y todo estaba bien. Recientemente estaba actualizando la función para no usar eval para la tranquilidad de la seguridad, y me encontré con un problema con objetos/funciones de espacio de nombres.

Específicamente traté de reemplazar un eval([name]) con un window[name] para acceder al objeto a través de la sintaxis del corchete cuadrado del objeto global frente a eval.

Pero me encontré con un problema con los objetos de espacios de nombres, por ejemplo:

var strObjName = 'namespace.serviceArea.function'; 
// if I do 
var obj = eval(strObjName); // works 

// but if I do 
var obj = window[strObjName]; // doesn't work 

Puede alguien llegar a una buena solución para evitar el uso de eval con las cadenas de espacios de nombres?

Respuesta

22

Puede dividir en . y resolver cada propiedad sucesivamente. Esto va a estar bien, siempre y cuando ninguno de los nombres de las propiedades de la cadena contiene un carácter .:

var strObjName = 'namespace.serviceArea.function'; 
var parts = strObjName.split("."); 
for (var i = 0, len = parts.length, obj = window; i < len; ++i) { 
    obj = obj[parts[i]]; 
} 
alert(obj); 
+0

@Tim esto es lo que estaba pensando, ¡pero lo tienes antes que yo! :) –

+0

¬¬ ¿Lo tienes primero? :(haha ​​+1 – JCOC611

+0

@TimDown mencionan cómo usar espacios de nombre como este no es realmente compatible de forma nativa y puede llevar a que el código se vuelva bastante complicado y difícil de mantener. Se siente como alguien que transfiere estilos de otros idiomas a JavaScript cuando hay mejores alternativas. – Raynos

2

me ocurrió esto:

function reval(str){ 
    var str=str.split("."),obj=window; 
    for(var z=0;z<str.length;z++){ 
    obj=obj[str[z]]; 
    } 
    return obj; 
} 
eval("window.this.that"); 
reval("this.that"); //would return the object 
reval("this.that")(); //Would execute 
-1

El siguiente se supone que las partes de strObjName están separados por . y bucles a través a partir de la ventana hasta que se pone manos a la función que desea:

var strObjParts = strObjName.split('.'); 
var obj = window; 
for(var i in strObjParts) { 
    obj = obj[strObjParts[i]]; 
} 
+1

'for ... in' no funcionará en general para este ejemplo en el que se requiere orden, ya que el orden de enumeración de' for ... in' no está garantizado. Use un ciclo 'for' normal. –

+0

No use for-in para recorrer arreglos en Javascript. Es malvado – hugomg

+0

@Tim: Gracias. No me di cuenta de que el orden no estaba garantizado. @missingno: No estoy seguro de lo que quieres decir con maldad. –

1

sé que esto ha sido contestada satisfact Oye al asker, pero recientemente tuve este problema, pero con ESCRIBIR al valor. Creé mi propia clase estática para manejar la lectura y la escritura basada en una ruta de cadena. El separador de ruta predeterminado es . pero puede modificarlo para que sea cualquier cosa, como /. El código también se comenta en caso de que te preguntes cómo funciona.

(function(){ 
    var o = {}, c = window.Configure = {}, seperator = '.'; 

    c.write = function(p, d) 
    { 
     // Split the path to an array and assaign the object 
     // to a local variable 
     var ps = p.split(seperator), co = o; 

     // Iterate over the paths, skipping the last one 
     for(var i = 0; i < ps.length - 1; i++) 
     { 
      // Grab the next path's value, creating an empty 
      // object if it does not exist 
      co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {}; 
     } 

     // Assign the value to the object's last path 
     co[ps[ps.length - 1]] = d; 
    } 

    c.read = function(p) 
    { 
     var ps = p.split(seperator), co = o; 
     for(var i = 0; i < ps.length; i++) 
     { 
      co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {}; 
     } 
     return co; 
    } 
})(); 
2

He escrito una solución diferente, utilizando recursividad. Estaba usando esto para nombrar el nombre del atributo de los elementos de entrada. Tome, por ejemplo, lo siguiente:

<input type="number" name="testGroup.person.age" /> 

Y digamos que establecemos el valor de esa entrada en "25". También tenemos un objeto en nuestro código en algún lugar:

var data = { 
    testGroup: { 
     person: { 
      name: null, 
      age: null, 
      salary: null 
     } 
    } 
}; 

lo tanto, el objetivo aquí es poner automáticamente el valor en nuestro elemento de entrada en la posición correcta en esa estructura de datos.

que he escrito esta pequeña función para crear un objeto anidado basado en lo que sea gran variedad de espacios de nombres que pase a ella:

var buildObject = function (obj, namespaceArray, pos) { 
    var output = {}; 

    output[namespaceArray[pos]] = obj; 

    if (pos > 0) { 
     output = buildObject(output, namespaceArray, pos-1); 
    } 

    return output; 
}; 

¿Cómo funciona? Comienza al final de la matriz namespaceArray y crea un objeto con una propiedad, cuyo nombre es el que se encuentre en la última ranura en namespaceArray, y cuyo valor sea obj.Luego se repite, envolviendo ese objeto en objetos adicionales hasta que se quede sin nombres en namespaceArray. pos es la longitud de la matriz namespaceArray. Podrías resolverlo en la primera llamada a la función, algo así como if (typeof(pos) === "undefined") pos = namespaceArray.length - 1, pero luego tendrías una declaración extra para evaluar cada vez que recursara la función.

primer lugar, se dividió el atributo name del input en una matriz alrededor de los separadores de espacio de nombres y obtener el valor de la entrada:

var namespaceArray = inputElement.name.split("."), 
    obj = inputElement.value; 

// Usually you'll want to do some undefined checks and whatnot as well 

Entonces sólo llamamos nuestra función y asignar el resultado a una variable:

var myObj = buildObject(obj, namespaceArray, namespaceArray.length - 1); 

myObj ahora se verá así:

{ 
    testGroup: { 
     person: { 
      age: 25 
     } 
    } 
} 

En este punto utilizo función de extensión de jQuery para fusionar esa estructura de nuevo en el objeto de datos original:

data = $.extend(true, data, myObj); 

Sin embargo, la fusión de dos objetos no es muy difícil de hacer en-marco libre de JavaScript y hay un montón de existing code eso hace bien el trabajo.

Estoy seguro de que hay formas más eficientes de hacerlo, pero este método satisface mis necesidades.

0

Mi muestra en Pastebin: http://pastebin.com/13xUnuyV

me gustó que el código, y lo hice un montón de cosas similar usando PHP. Pero aquí fue difícil como pensé ... Así que utilicé las respuestas @LordZardeck (https://stackoverflow.com/a/9338381/1181479) y @Alnitak (https://stackoverflow.com/a/6491621/1181479).

Y aquí lo que tengo, sólo lo utilizan:

var someObject = { 
     'part1' : { 
      'name': 'Part 1', 
      'size': '20', 
      'qty' : '50' 
     }, 
     'part2' : { 
      'name': 'Part 2', 
      'size': '15', 
      'qty' : '60' 
     }, 
     'part3' : [ 
      { 
       'name': 'Part 3A РУКУ!!!', 
       'size': '10', 
       'qty' : '20' 
      }, { 
       'name': 'Part 3B', 
       'size': '5', 
       'qty' : '20' 
      }, { 
       'name': 'Part 3C', 
       'size': '7.5', 
       'qty' : '20' 
      } 
     ] 
    }; 

    //var o = {}, c = window.Configure = {}, seperator = '.'; 

    var c = function(){ 
     this.o = {}; 
     this.seperator = "."; 
     this.set = function(obj){ 
      this.o = obj; 
     } 
     this.write = function(p, d) { 
      p = p.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties 
      p = p.replace(/^\./, ''); // strip leading dot 
      // Split the path to an array and assaign the object 
      // to a local variable 
      var ps = p.split(this.seperator), co = this.o; 

      // Iterate over the paths, skipping the last one 
      for(var i = 0; i < ps.length - 1; i++) 
      { 
       // Grab the next path's value, creating an empty 
       // object if it does not exist 
       co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {}; 
      } 

      // Assign the value to the object's last path 
      co[ps[ps.length - 1]] = d; 
     } 
     this.read = function(p) { 
      p = p.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties 
      p = p.replace(/^\./, ''); // strip leading dot 
      var ps = p.split(this.seperator), co = this.o; 
      /* 
      for(var i = 0; i < ps.length; i++) 
      { 
       co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {}; 
      } 
      */ 
      while (ps.length) { 
       var n = ps.shift(); 
       if (n in co) { 
        co = co[n]; 
       } else { 
        return; 
       } 
      } 
      return co; 
     } 

    }; 

    var n = new c(); 
    n.set(someObject); 
    console.log('whas'); 
    console.log('n.read part.name', n.read('part1.name')); 
    n.write('part3[0].name', "custom var"); 
    console.log('part1.name now changed'); 
    n.write('part1.name', "tmp"); 
    console.log('n.read part.name', n.read('part1.name')); 
    console.log('----'); 
    console.log('before', someObject); 
    console.log('someObject.part1.name', someObject.part1.name); 
    console.log('someObject.part3[0].name', someObject.part3[0].name); 
6

Sólo pensé en compartir esto porque he hecho esto el otro día. ¡Ni siquiera me di cuenta de que había reducción en JS!

function getByNameSpace(namespace, obj) { 
    return namespace.split('.').reduce(function(a,b) { 
      if(typeof a == 'object') return a[b]; 
      else return obj[a][b]; 
    }); 
} 

la esperanza que alguien encuentre esto útil ..

+0

¡Me gusta la idea! Ligeramente adaptado para mis propósitos .. http://codepen.io/anon/pen/taExD/ –

0

Es posible hacer esto utilizando técnicas de programación funcionales tales como

(function (s) {return s.split('.').reduce(function(p,n) { p[n] = p[n] || {}; return p[n];},window);})("some.cool.namespace"); 

Esto se puede asignar a una función global para su reutilización

window.ns = (function (s) {return s.split('.').reduce(function(p,n) { p[n] = p[n] || {}; return p[n];},window);}) 

Entonces podemos hacer lo siguiente

ns("some.cool").namespace = 5; 

if (5 != ns("some.cool.namespace")) { throw "This error can never happen" } 
Cuestiones relacionadas