从字符串引用Javascript对象

Reference Javascript object from string

本文关键字:对象 Javascript 引用 字符串      更新时间:2023-09-26

给定的数据类似于:

var data = [{id: 12345, 
    name:'my products', 
    items:[{ 
               size: 'XXL', 
               sku: 'awe2345', 
               prices:[{type: 'rrp',prices: 10.99}, 
                        {type: 'sell_price', price: 9.99}, 
                         {type:'dealer', price:4.50} 
               ] 
               },{ 
               size: 'XL', 
               sku: 'awe2346', 
               prices:[{type: 'rep', prices: 10.99}, 
                          {type: 'sell_price', price: 9.99}, 
                          {type:'dealer', price:4.50} 
               ] 
               } 
       ] 
   }] 
}]

是否有一种方法来评估数据对象中元素的字符串表示形式?例如:"data[0].items[0].prices[0]."rrp"…不使用eval()?

理想的解决方案是首先不使用字符串表示。认真地问自己是否更改了现有的代码以使您的内容在更理想的输出中

然而,如果你不能,这应该做你想要的:

var path = "data[0].items[0].prices[0].rrp".split(/['[']'.]+/);
var next = window;
if (path[path.length - 1] == "") {
    path.pop();
};
while (path.length && (next = next[path.shift()]) && typeof next == "object" && next !== null);
next;

创建一个函数:

function get(path) {
    var next = window;
    path = path.split(/['[']'.]+/);
    if (path[path.length - 1] == "") {
        path.pop();
    };
    while (path.length && (next = next[path.shift()]) && typeof next === "object" && next !== null);
    return path.length ? undefined : next;
}

缺点:

  1. 变量data必须在全局作用域中才能工作(不能是局部变量)

  2. 这是讨厌的。

Edit:要使用设置功能,您可以像下面这样滥用JavaScript对象的引用传递特性:

function set(path, value) {
    var split = Math.max(path.lastIndexOf("["), path.lastIndexOf("."));
    get(path.slice(0, split))[path.slice(split + 1).replace(/']/, "")] = value;
}

提供一些工作原理的解释:

getter首先将输入分割成一个数组,这样每个元素都是我们需要遍历[data, 0, items, 0, prices, 0, rrp]的成员。如果搜索字符串以"]"结尾,我们在数组的末尾得到一个额外的空元素,因此我们检查并删除它。

然后进行大循环;因此,当我们有元素要遍历(while path.length)时,将next变量设置为我们需要遍历next = next[path.shift()]的下一个对象成员。检查它是否为一个对象(否则它将没有任何成员可遍历),并检查它是否为null(因为typeof null == "object")。

一旦循环执行完毕,就得到了链中的最后一个元素;所以返回它

setter在搜索字符串中搜索最后一个对象引用,并使用get()函数检索该对象引用。然后将返回的对象键设置为所需的值。如果我们不这样做,我们就会设置一个值而不是一个引用,所以"真正的"对象永远不会被更新。

Matt的解决方案是纯粹的天才-但是还有一些改进的空间:

  • 如果'path'没有分隔符,解决方案失败;set('h1', somevalue)将失败,因为在这种情况下,路径分割失败
  • 只支持全局作用域
  • 如果你有一个具有obj.here.is.a.value = "I am a value"的对象,并且你请求obj.here.is.a.value.no.value -它仍然会返回"I am a value"

这是我对这三个问题的解决方案:

    getPropertyValueByPath : function(obj, path)
    {
        path = path.split(/['[']'.]+/);
        if(path[path.length - 1] == "")
        {
            path.pop();
        };
        while(path.length && ( obj = obj[path.shift()]));
        return obj;
    }

setPropertyValuebyPath : function(obj, path, value)
    {
        var pathElements = path.replace(/'[|']/g, '.').replace(/'.+/g, '.').split(/'./)
        pathEnd = pathElements[pathElements.length - 1]
        pathRoot = (pathElements.slice(0, pathElements.length - 1).join('.'))
        var currObj = obj
        for(var i = 0; i < pathElements.length; i++)
        {
            if( typeof (currObj[pathElements[i]]) == 'undefined')
            {
                currObj[pathElements[i]] = {}
            }
            currObj = currObj[pathElements[i]]
        }
        // This line by Matt is genious :)
        getPropertyValueByPath(obj, pathRoot)[pathEnd] = value
        return true
    }

这样称呼它们:

var joe = {}
setPropertyValueByPath(joe,'the[1].long.and[2].road', 'yeah')
// joe.the[1].long.and[2].road now has a value of 'yeah' - 
// all the missing objects were created
// this will alert 'yeah'
alert( getPropertyValueByPath(joe,'the[1].long.and[2].road') )

我不确定我是否理解这个问题,但我猜你想在内部数组中"选择"该对象?

如果是这种情况,你需要循环遍历那个内部数组。

Object.keys( data[0].items[0].prices[0] ).some(function( obj ) {
    if( obj.type === 'rrp' ) {
        // do something with that object here
        return true;
    }
});

要使代码符合ES3,我们将使用:

var target = data[0].items[0].prices[0],
    len    = target.length;
for(var i = 0; i < len; i++) {
    if( target[i].type === 'rrp' ) {
        // do something with target[i]
        break;
    } 
}

如果我回答错了,很抱歉占用你的时间:-)

如果您使用getter而不是公开的公共字段,则可以在首次返回集合之前进行一些后处理:

   var data = [{
        id: 12345,
        name: 'my products',
        items: [{
            size: 'XXL',
            sku: 'awe2345',
            prices: [{ type: 'rrp', price: 10.99 },
                    { type: 'sell_price', price: 9.99 },
                    { type: 'dealer', price: 4.50 }
                   ]
        }, {
            size: 'XL',
            sku: 'awe2346',
            prices: [{ type: 'rep', price: 10.99 },
                            { type: 'sell_price', price: 9.99 },
                            { type: 'dealer', price: 4.50}]
        }
            ],
        getItems: function () {
            if (!this._applied) {
                this._applied = true;
                for (var i = 0; i <  this.items.length; i++) {
                    this.items[i].prices = this._asCrossReferencable(this.items[i].prices);
                }
            }
            return this.items;
        },
        _asCrossReferencable: function (priceArr) {
            for (var i = 0; i < priceArr.length; i++) {
                priceArr[i][priceArr[i].type] = priceArr[i].price;
            }
            return priceArr;
        },
        _applied: false
    }];
    var items = data[0].getItems();
    for(var i = 0; i < items.length; i++) {
        for(var p = 0; p < items[i].prices.length; p++){
            var price = items[i].prices[p];
            alert(price[price.type]);
        }
    }

对于一些懒惰的速记来说,这是一个很大的膨胀。