Javascript对象键值编码。动态设置嵌套值

Javascript对象键值编码。动态设置嵌套值,javascript,object,key-value-observing,key-value-coding,Javascript,Object,Key Value Observing,Key Value Coding,我正在开发一个小库,它可以让我对对象进行一些基本的键值编码。假设我有以下目标: var data = { key1: "value1", key2: { nested1: 1, nested2: "wowza!" } }; 我有以下JavaScript函数: var setData = function(path, value) { eval("data." + path + "= value;"); }; 在使用中: setData("key1", "updated value1"

我正在开发一个小库,它可以让我对对象进行一些基本的键值编码。假设我有以下目标:

var data = { key1: "value1", key2: { nested1: 1, nested2: "wowza!" } };
我有以下JavaScript函数:

var setData = function(path, value) {
    eval("data." + path + "= value;");
};
在使用中:

setData("key1", "updated value1"); // data.key1 == "updated value1"
setData("key2.nested1", 99); // data.key2.nested1 == 99
这是可行的,但是我希望在不使用
eval
的情况下完成上述操作。这是可能的,还是
eval
最好的方法

编辑:


注意可以假设您设置的值存在,至少路径深度为-1。我更关心的是设置现有对象的值。

是的,这很简单
obj.prop=val
obj['prop']=val
相同,因此您可以将setData重写为:

var setData = function (path, value) {
    data[path] = value;
};
这应该可以做到

但是,我不确定您在第三级中会取得多大成功(如果有意义的话)。obj.prop.prop不能被obj['prop.prop']引用,而是必须被obj['prop']['prop']引用,因此必须重写setData才能考虑到这一点。不过,我在这方面运气不太好

编辑:我已经做了一个(为我)设置它嵌套的方式,你想要的,但没有更多。不幸的是,如果您的嵌套比示例更深,那么我看不到放弃
eval
的真正原因。我还没有做过任何基准测试,但在某些情况下,计算的计算成本甚至比
eval
还要高(这很难实现)

这就是我想出来的,这将使他们至少有1级的深度;也就是说,它将使用
key2.nested1
,但不使用
key2.nested1.i_love_nesting
,如果这有意义的话:

var setData = function (path, value) {
    if (path.indexOf('.') != -1) {
        path = path.split('.');
        for (var i = 0, l = path.length; i < l; i++) {
            if (typeof(data[path[i]]) === 'object') {
                continue;
            } else {
                data[path[i - 1]][path[i]] = value;
            }
        }
    } else {
        data[path] = value;
    }
};
var setData=函数(路径、值){
if(path.indexOf('.')!=-1){
path=path.split('.');
对于(变量i=0,l=path.length;i

希望这有帮助。我可能没有用最有效的方式写这篇文章,但是

递归是您需要的:

function setData(key,val,obj) {
  if (!obj) obj = data; //outside (non-recursive) call, use "data" as our base object
  var ka = key.split(/\./); //split the key by the dots
  if (ka.length < 2) { 
    obj[ka[0]] = val; //only one part (no dots) in key, just set value
  } else {
    if (!obj[ka[0]]) obj[ka[0]] = {}; //create our "new" base obj if it doesn't exist
    obj = obj[ka.shift()]; //remove the new "base" obj from string array, and hold actual object for recursive call
    setData(ka.join("."),val,obj); //join the remaining parts back up with dots, and recursively set data on our new "base" obj
  }    
}

setData("key1", "updated value1"); // data.key1 == "updated value1"
setData("key2.nested1", 99); // data.key2.nested1 == 99
功能设置数据(按键、val、obj){
如果(!obj)obj=data;//外部(非递归)调用,请使用“data”作为基本对象
var ka=key.split(//\.//);//按点分割键
如果(ka.length<2){
obj[ka[0]]=val;//键中只有一个部分(没有点),只需设置值
}否则{
if(!obj[ka[0]])obj[ka[0]]={};//如果我们的“新”基础对象不存在,请创建它
obj=obj[ka.shift()];//从字符串数组中删除新的“base”obj,并保留实际对象以进行递归调用
setData(ka.join(“.”,val,obj);//用点连接其余部分,并递归地在新的“基本”obj上设置数据
}    
}
设置数据(“键1”,“更新值1”);//data.key1==“更新的值1”
setData(“key2.nested1”,99);//data.key2.nested1==99

我认为,如果您能够灵活地指定路径,这个问题将变得更容易、更自然地解决。如果不执行类似于
key2.nested1
的操作,而可以执行
{key2:{nested1:{}}}
(对象表示法),那么它就变得相当简单:

function setData(subtree, path){
    var nodeName = Object.keys(path);

    if(typeof path[nodeName] == 'object')
        setData(subtree[nodeName], path[nodeName]);
    else
        subtree[nodeName] = path[nodeName];
}

var data = { key1: "value1", key2: { nested1: 1, nested2: "wowza!" } };

// For the initial call, 'subtree' is the full data tree
setData(data, {key2:{nested1:99}});
您只需递归到
setData
调用的第二个参数,直到找到值为止。每次递归时,您都会在指定的点下放到
数据
树中

这个函数也可以很容易地修改为接受您指定的点表示法,但对我来说,这是一种更干净、更自然的方法(另外,字符串运算速度很慢)


干杯

灵感来自@user1416920我做了这个:

function setData(key,val,obj)
{
    keys = key.split(/\./);
    last = keys.pop();

    keys.forEach(function(key)
    {
      if(typeof obj[key] === "undefined")
        obj[key] = {}; 
      obj = obj[key]; 
    });

    obj[last] = val;
}

它所做的事情是不言自明的^^。

不幸的是,这对嵌套值不起作用。在您的示例中:setData(“prop.nestedProp”、“val”);将产生以下数据json:{“prop.nestedProp”:“val”}我想要:{prop:{nestedProp:{nestedProp:val”}是的,我在提交帖子几秒钟后发现了这一点。我花了一点时间才得到一个很好的工作函数,但上面的那个应该可以。您的库的另一种选择可能是让他们直接访问“对象”,即让他们访问
my_json\u object.foo.bar
。如果值发生更改,则完整函数(为简单起见排除)将触发事件<代码>app.dataSet=function(path,value){if(app.dataGet(path)==value)返回值;eval(“app.u数据”+path+“=value;”);app.trigger(“数据”+path);返回app.dataGet(path);}不幸的是,这需要在任意深度工作。我希望有一个很好的解决方案来解决这个问题,让get函数(必须测试它是否比使用eval更快)。谢谢你的帮助。我希望我能投100票——这正是我想要的!非常感谢。非常感谢你!我无法告诉你这对我有多大帮助。这是一个很好的答案。我制作了一个没有
obj
参数的版本,并且为了清晰起见使用了ES6语法。非常感谢你。你救了我一天!
function setData(key,val,obj)
{
    keys = key.split(/\./);
    last = keys.pop();

    keys.forEach(function(key)
    {
      if(typeof obj[key] === "undefined")
        obj[key] = {}; 
      obj = obj[key]; 
    });

    obj[last] = val;
}