2

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

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。我更关心设置现有对象的值。

4

5 回答 5

18

递归是你需要的:

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
于 2010-01-14T02:38:52.533 回答
4

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

var setData = function (path, value) {
    data[path] = value;
};

那应该这样做。

但是,我不确定您将在第 3 级取得多大成功(如果这有意义的话。) obj.prop.prop 不能被 obj['prop.prop'] 引用,而是必须被obj['prop']['prop'],因此必须重写 setData 以考虑到这一点。不过,我对此并不走运。

编辑:我已经制作了一个(对我而言)将它设置为您想要的嵌套方式,但仅此而已。不幸的是,如果你的嵌套比你的例子更深,那么我看不出放弃的真正理由eval。我没有做过任何基准测试,但在某些时候,计算的计算成本甚至会更高eval(这很难完成。)

这就是我想出的,这将使它们至少设置 1 个“级别”深度;也就是说,如果有意义的话,它将与key2.nested1but not一起使用: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;
    }
};

希望这可以帮助。我可能没有以最有效的方式写这个,虽然......

于 2010-01-14T00:16:13.943 回答
1

受@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;
}

它的作用是不言自明的^^。

于 2016-12-30T00:16:39.567 回答
0
function setData(key,val,obj){
    keys = key.split(/\./);
    to_set = keys.pop();
    keys.forEach(function(obj_name){ obj = obj[obj_name]; });
    obj[to_set] = val;
}
于 2012-05-25T08:33:15.263 回答
0

如果您可以灵活地指定路径,我认为这个问题会变得更容易、更自然地解决。如果不是做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调用的第二个参数,直到找到该值。每次递归时,您都会data在指定点下到树中。

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

干杯

于 2013-10-26T16:14:45.530 回答