通过点语法字符串路径访问对象

3
如何通过字符串“path.to.obj”访问myobject[path][to][obj]?我想调用一个函数Settings.write('path.to.setting', 'settingValue'),它将'settingValue'写入settingObj[path][to][setting]。我如何在不使用eval()的情况下实现这一点?我最终在与下面的一个用户回答它的同时弄清了这个问题。如果有人感兴趣,我会在此处发布我的文档以说明它的工作原理。
(function(){
    var o = {}, c = window.Configure = {};

    c.write = function(p, d)
    {
        // Split the path to an array and assaign the object
        // to a local variable
        var ps = p.split('.'), 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('.'), co = o;
        for(var i = 0; i < ps.length; i++)
        {
            co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {};
        }
        return co;
    }
})();

我遇到问题的原因是你需要跳过最后一个路径。如果包括最后一个路径,你最终只会分配一个任意对象。
2个回答

3
您可以使用 str.split() 来实现这个功能:
path = pathString.split('.');
settingObj[path[0]][path[1]][path[2]] = settingValue;

str.split()将变量分割成一个数组,根据您指定的字符位置进行分割。

当然,这只是操作的基础,假设输入正确。

编辑:由于路径的长度需要是可变的,因此这是从您上面显示的语法编辑的更新版本:

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

    c.write = function(p, d)
    {
        var ps = p.split('.'), co = o;
        for(var i = 0; i < ps.length - 1; i++)
        {
            co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {};
        }
        co[ps[ps.length-1]] = d;
    }

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

    c.write('path.to.property', 'setting');
    alert(c.read('path.to.property'));
})();

但你永远无法确切知道路径有多少部分。可能是2个,也可能是6个。 - LordZardeck
我的错,我以为那是某种预设设置或其他什么东西。我在这里放了代码:http://jsfiddle.net/HVh7X/,在看到你的编辑之前我就开始了,所以我会在一分钟内更新我的答案。 - Rick
哈哈,是的,我刚刚想通了。我需要跳过最后一个路径,因为我只是分配了一个任意值。一旦你编辑好你的答案,我就会接受它。 - LordZardeck
警告:您上面的代码将覆盖任何对象,即使您试图追加它。第9行和第10行应更改为co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {}; - LordZardeck
好的观点。我没有考虑到预先存在的设置路径。已经编辑过了,现在应该没问题了。无论如何,感谢您将其标记为答案。 :) - Rick

2
您的变量co包含对属性的引用,但当您使用co = p更新co的值时,您只是覆盖了co的值,而不是属性的值。 您需要保留对最后一个键的引用,并使用该键设置值: http://jsfiddle.net/gilly3/a2p8m/1/
c.write = function(p, d) 
{ 
    var ps = p.split('.'), co = o, key; 
    for(var i = 0; i < ps.length; i++) 
    { 
        if (key)
        {
            co = (key in co) ? co[key] : co[key] = {};
        }
        key = ps[i];
    } 
    co[key] = d; 
    console.log(co); 
    console.log(o); 
} 

您可能希望使用key in co而不是仅进行“真值”检查。否则,1的值将被保留,而0的值将被覆盖为{}。然而,以上代码中的任何一个值都会导致运行时错误,因为您无法将属性写入数字上。最好检查typeof co[key]

co = co[key] && /object|function/.test(typeof co[key]) ? co[key] : co[key] = {};

我知道我的回答有点晚,但我认为在用我的大拇指打字花费了那么多精力之后,还是把它包含进来吧。
我猜你遇到了一个分组错误。尝试将三元表达式中的右括号移到行末:
co = (co[ps[i]] ? co[ps[i]] : co[ps[i]] = {});

我希望你能帮我翻译这段话,但是我现在正在用手机回答问题,如果要在jsfiddle上测试的话,只用双手大力敲打会很麻烦。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接