2011-12-07 4 views
6

Actualmente estoy trabajando en un complemento con una variable de configuración bastante profunda (3-4 niveles en algunos lugares). Siguiendo el patrón de jQuery Plugin generalmente aceptada he implementado una forma sencilla para que los usuarios modificar los ajustes sobre la marcha utilizando la siguiente notación:Complemento jQuery - Modificación de opción profunda

$('#element').plugin('option', 'option_name', 'new_value'); 

Este es el código similar a lo que estoy utilizando ahora para el método de opciones.

option: function (option, value) { 
    if (typeof (option) === 'string') { 
     if (value === undefined) return settings[option]; 

     if(typeof(value) === 'object') 
      $.extend(true, settings[option], value); 
     else 
      settings[option] = value; 
    } 

    return this; 
} 

Ahora considero que tengo una variable de configuración de este modo:

var settings = { 
    opt: false, 
    another: { 
     deep: true 
    } 
}; 

Si quiero cambiar las deep ajustes que tiene que utilizar la siguiente notación:

$('#element').plugin('option', 'another', { deep: false }); 

Sin embargo, ya que en la práctica mis configuraciones pueden tener 3-4 niveles de profundidad, creo que la siguiente notación sería más útil:

$('#element').plugin('option', 'another.deep', false); 

Sin embargo, no estoy seguro de cuán factible es esto ni de cómo hacerlo. Como primer intento, traté de "atravesar" la opción en cuestión y configurarla, pero si configuro la variable de desplazamiento, no establece a qué se refiere en la variable de configuración original.

option: function (option, value) { 
    if (typeof (option) === 'string') { 
     if (value === undefined) return settings[option]; 

     var levels = option.split('.'), 
      opt = settings[levels[0]]; 
     for(var i = 1; i < levels.length; ++i) 
      opt = opt[levels[i]]; 

     if(typeof(value) === 'object') 
      $.extend(true, opt, value); 
     else 
      opt = value; 
    } 

    return this; 
} 

Decir que de otra manera: Al establecer opt después de atravesar, el ajuste en realidad se refiere a la variable settings no ha cambiado después de este código se ejecuta.

Pido disculpas por la larga pregunta, cualquier ayuda es apreciada. ¡Gracias!


EDITAR

Como un segundo intento puedo hacerlo usando eval() así:

option: function (option, value) { 
    if (typeof (option) === 'string') { 
     var levels = option.split('.'), 
      last = levels[levels.length - 1]; 

     levels.length -= 1; 

     if (value === undefined) return eval('settings.' + levels.join('.'))[last]; 

     if(typeof(value) === 'object') 
      $.extend(true, eval('settings.' + levels.join('.'))[last], value); 
     else 
      eval('settings.' + levels.join('.'))[last] = value; 
    } 

    return this; 
} 

Pero realmente me gustaría ver si alguien me puede mostrar una forma de no use eval. Como es una cadena de entrada de usuario, prefiero no ejecutar eval() porque podría ser cualquier cosa. O avíseme si estoy siendo paranoico y no debería causar ningún problema.

Respuesta

2

La cuestión que se está ejecutando en que aquí se reduce a la diferencia entre las variables que apuntan a objetos y variables para otros tipos como Strings.2 variables pueden apuntar al mismo Object, pero no en la misma String:

var a = { foo: 'bar' }; 
    var b = 'bar'; 
    var a2 = a; 
    var b2 = b; 
    a2.foo = 'hello world'; 
    b2 = 'hello world'; 
    console.log(a.foo); // 'hello world' 
    console.log(b); // 'bar' 

Su código recorrido funciona muy bien hasta que la última iteración del bucle, y en ese momento opt es una variable que contiene el mismo valor como deep dentro del objeto settings.opt.another. En su lugar, cortar el lazo pequeño y utilizar el último elemento de levels como key, como

var settings = { 
    another: { 
     deep: true 
    } 
    }; 

    var levels = 'another.deep'.split('.') 
    , opt = settings; 

    // leave the last element 
    var i = levels.length-1; 

    while(i--){ 
    opt = opt[levels.shift()]; 
    } 
    // save the last element in the array and use it as a key 
    var k = levels.shift(); 
    opt[k] = 'foobar'; // settings.another.deep is also 'foobar' 

En esta etapa opt es un puntero a la misma Object como settings.another y k es un String con el valor 'deep'

+0

Fantástico, la confusión en las referencias era lo que nublaba el problema. Gracias por aclarar eso para mí; Tengo un método de trabajo ahora gracias a tu respuesta. – Chad

0

¿Qué le parece usar eval en lugar de transversal?

var settings = { 
opt: false, 
    another: { 
deep: true, 

    } 
}; 

var x = "settings.another"; 
eval(x).deep = false; 
alert(settings.another.deep); 
+1

Estaba tratando de evitar el uso de 'eval' en una cadena de entrada de usuario, ya que su uso de esta manera hace que mi complemento acepte javascript inyectado ... – Chad

+0

No hay seguridad en el lado del navegador de todos modos. Un usuario puede ejecutar cualquier js que quiera y enviar cualquier consulta. –

0

¿Sería el construido en jQuery $().extend() no sea exactamente lo que necesita?

http://api.jquery.com/jQuery.extend/

* Nota de la segunda firma del método con el primer agument de true realiza una fusión profunda ...

+0

Es cierto, pero significa que tendría que usar la sintaxis que espera reemplazar aquí. El objetivo es acceder a esas variables con notación de puntos en un argumento de cadena a la función. –

+0

Como dijo Daniel, estoy usando el método extendido si el usuario pasa un objeto, sin embargo, ellos tienen que pasar un objeto para ajustes profundos. Estoy tratando de permitirles la opción de una cadena también. – Chad