我有一个这样的数据结构:

var someObject = {
    'part1' : {
        'name': 'Part 1',
        'size': '20',
        'qty' : '50'
    },
    'part2' : {
        'name': 'Part 2',
        'size': '15',
        'qty' : '60'
    },
    'part3' : [
        {
            'name': 'Part 3A',
            'size': '10',
            'qty' : '20'
        }, {
            'name': 'Part 3B',
            'size': '5',
            'qty' : '20'
        }, {
            'name': 'Part 3C',
            'size': '7.5',
            'qty' : '20'
        }
    ]
};

我想使用这些变量访问数据:

var part1name = "part1.name";
var part2quantity = "part2.qty";
var part3name1 = "part3[0].name";

part1name应该用someObject.part1.name的值填充,即“Part 1”。part2quantity也是一样,它的容量是60。

有没有办法实现这与纯javascript或JQuery?


当前回答

数组可以代替字符串来处理嵌套对象和数组,例如:["my_field", "another_field", 0, "last_field", 10]

下面是一个基于该数组表示方式更改字段的示例。我在react.js中使用类似的东西来控制输入字段,改变嵌套结构的状态。

let state = {
        test: "test_value",
        nested: {
            level1: "level1 value"
        },
        arr: [1, 2, 3],
        nested_arr: {
            arr: ["buh", "bah", "foo"]
        }
    }

function handleChange(value, fields) {
    let update_field = state;
    for(var i = 0; i < fields.length - 1; i++){
        update_field = update_field[fields[i]];
    }
    update_field[fields[fields.length-1]] = value;
}

handleChange("update", ["test"]);
handleChange("update_nested", ["nested","level1"]);
handleChange(100, ["arr",0]);
handleChange('changed_foo', ["nested_arr", "arr", 3]);
console.log(state);

其他回答

根据阿尔尼塔克的回答。

我把填充材料包在格子里,将函数简化为单链还原。

如果对象。byPath === undefined) { 对象。byPath = (obj, path) =>路径 .replace (/ \ [(\ w +) \] / g, 1美元。) .replace(/ ^ \。/”) .split (/ \ / g)。 .reduce((ref, key) => key in ref ?Ref [key]: Ref, obj) } Const data = { foo: { 酒吧:[{ 记者:1 }) } } console.log(对象。小路(数据,foo.bar [0] .baz '))

简单的函数,允许字符串或数组路径。

function get(obj, path) {
  if(typeof path === 'string') path = path.split('.');

  if(path.length === 0) return obj;
  return get(obj[path[0]], path.slice(1));
}

const obj = {a: {b: {c: 'foo'}}};

console.log(get(obj, 'a.b.c')); //foo

OR

console.log(get(obj, ['a', 'b', 'c'])); //foo

使用下划线的属性或propertyOf:

Var检验= { foo: { 酒吧:{ 记者:“你好” } } } Var字符串= 'foo.bar.baz'; / / document . write (_.propertyOf(测试)(string.split (' . '))) document . write (_.property (string.split(' . '))(测试); < script src = " https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js " > < /脚本>

祝你好运…

在这里我提供了更多的方法,这些方法在很多方面看起来都更快:

选项1:Split string on。Or [Or] Or ' Or ",颠倒过来,跳过空项。

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var parts = path.split(/\[|\]|\.|'|"/g).reverse(), name; // (why reverse? because it's usually faster to pop off the end of an array)
    while (parts.length) { name=parts.pop(); if (name) origin=origin[name]; }
    return origin;
}

选项2(最快的,除了eval):低级字符扫描(没有regex/split/等等,只是一个快速字符扫描)。 注意:该命令不支持索引引用。

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var c = '', pc, i = 0, n = path.length, name = '';
    if (n) while (i<=n) ((c = path[i++]) == '.' || c == '[' || c == ']' || c == void 0) ? (name?(origin = origin[name], name = ''):(pc=='.'||pc=='['||pc==']'&&c==']'?i=n+2:void 0),pc=c) : name += c;
    if (i==n+2) throw "Invalid path: "+path;
    return origin;
} // (around 1,000,000+/- ops/sec)

选项3:(新:选项2扩展到支持引号-有点慢,但仍然很快)

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var c, pc, i = 0, n = path.length, name = '', q;
    while (i<=n)
        ((c = path[i++]) == '.' || c == '[' || c == ']' || c == "'" || c == '"' || c == void 0) ? (c==q&&path[i]==']'?q='':q?name+=c:name?(origin?origin=origin[name]:i=n+2,name='') : (pc=='['&&(c=='"'||c=="'")?q=c:pc=='.'||pc=='['||pc==']'&&c==']'||pc=='"'||pc=="'"?i=n+2:void 0), pc=c) : name += c;
    if (i==n+2 || name) throw "Invalid path: "+path;
    return origin;
}

JSPerf: http://jsperf.com/ways-to-dereference-a-delimited-property-string/3

"eval(...)" is still king though (performance wise that is). If you have property paths directly under your control, there shouldn't be any issues with using 'eval' (especially if speed is desired). If pulling property paths "over the wire" (on the line!? lol :P), then yes, use something else to be safe. Only an idiot would say to never use "eval" at all, as there ARE good reasons when to use it. Also, "It is used in Doug Crockford's JSON parser." If the input is safe, then no problems at all. Use the right tool for the right job, that's it.

AngularJS

Speigg的方法非常简洁,尽管我在搜索通过字符串路径访问AngularJS $scope属性的解决方案时发现了这个回复,并进行了一些修改:

$scope.resolve = function( path, obj ) {
    return path.split('.').reduce( function( prev, curr ) {
        return prev[curr];
    }, obj || this );
}

只需要把这个函数放在你的根控制器中,并像这样使用它的任何子范围:

$scope.resolve( 'path.to.any.object.in.scope')