我正在开发一个小库,可以让我用对象做一些基本的键值编码。说我有以下对象:
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.我更关心设置现有对象的值。
答案 0 :(得分:17)
递归就是你所需要的:
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
答案 1 :(得分: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.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;
}
};
希望这会有所帮助。我可能没有以最有效的方式写这个,但是......
答案 2 :(得分:1)
受到@ user1416920的启发我做了这个:
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.1.1.min.js"></script>
它的作用非常自我解释^^。
答案 3 :(得分: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;
}
答案 4 :(得分: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
调用的第二个arg,直到找到该值。每次递归时,您都会在指定点下降到data
树。
这个函数也很容易被修改为接受你指定的点符号,但对我来说这是一种更清晰,更自然的方法(另外,String ops很慢)。
干杯