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

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?


当前回答

也适用于对象内的数组/数组。 防御无效值。

/** * Retrieve nested item from object/array * @param {Object|Array} obj * @param {String} path dot separated * @param {*} def default value ( if result undefined ) * @returns {*} */ function path(obj, path, def){ var i, len; for(i = 0,path = path.split('.'), len = path.length; i < len; i++){ if(!obj || typeof obj !== 'object') return def; obj = obj[path[i]]; } if(obj === undefined) return def; return obj; } ////////////////////////// // TEST // ////////////////////////// var arr = [true, {'sp ace': true}, true] var obj = { 'sp ace': true, arr: arr, nested: {'dotted.str.ing': true}, arr3: arr } shouldThrow(`path(obj, "arr.0")`); shouldBeDefined(`path(obj, "arr[0]")`); shouldBeEqualToNumber(`path(obj, "arr.length")`, 3); shouldBeTrue(`path(obj, "sp ace")`); shouldBeEqualToString(`path(obj, "none.existed.prop", "fallback")`, "fallback"); shouldBeTrue(`path(obj, "nested['dotted.str.ing'])`); <script src="https://cdn.rawgit.com/coderek/e7b30bac7634a50ad8fd/raw/174b6634c8f57aa8aac0716c5b7b2a7098e03584/js-test.js"></script>

其他回答

也适用于对象内的数组/数组。 防御无效值。

/** * Retrieve nested item from object/array * @param {Object|Array} obj * @param {String} path dot separated * @param {*} def default value ( if result undefined ) * @returns {*} */ function path(obj, path, def){ var i, len; for(i = 0,path = path.split('.'), len = path.length; i < len; i++){ if(!obj || typeof obj !== 'object') return def; obj = obj[path[i]]; } if(obj === undefined) return def; return obj; } ////////////////////////// // TEST // ////////////////////////// var arr = [true, {'sp ace': true}, true] var obj = { 'sp ace': true, arr: arr, nested: {'dotted.str.ing': true}, arr3: arr } shouldThrow(`path(obj, "arr.0")`); shouldBeDefined(`path(obj, "arr[0]")`); shouldBeEqualToNumber(`path(obj, "arr.length")`, 3); shouldBeTrue(`path(obj, "sp ace")`); shouldBeEqualToString(`path(obj, "none.existed.prop", "fallback")`, "fallback"); shouldBeTrue(`path(obj, "nested['dotted.str.ing'])`); <script src="https://cdn.rawgit.com/coderek/e7b30bac7634a50ad8fd/raw/174b6634c8f57aa8aac0716c5b7b2a7098e03584/js-test.js"></script>

ES6:只有一行在Vanila JS(它返回null如果没有找到,而不是给出错误):

'path.string'.split('.').reduce((p,c)=>p&&p[c]||null, MyOBJ)

或例子:

'a.b.c'.split('.').reduce((p,c)=>p&&p[c]||null, {a:{b:{c:1}}})

可选链接操作符:

'a.b.c'.split('.').reduce((p,c)=>p?.[c], {a:{b:{c:1}}})

对于一个也识别false, 0和负数并接受默认值作为参数的ready to use函数:

const resolvePath = (object, path, defaultValue) => path
   .split('.')
   .reduce((o, p) => o ? o[p] : defaultValue, object)

示例:

resolvePath(window,'document.body') => <body>
resolvePath(window,'document.body.xyz') => undefined
resolvePath(window,'document.body.xyz', null) => null
resolvePath(window,'document.body.xyz', 1) => 1

奖金:

要设置路径(由@rob-gordon请求),您可以使用:

const setPath = (object, path, value) => path
   .split('.')
   .reduce((o,p,i) => o[p] = path.split('.').length === ++i ? value : o[p] || {}, object)

例子:

let myVar = {}
setPath(myVar, 'a.b.c', 42) => 42
console.log(myVar) => {a: {b: {c: 42}}}

使用[]访问数组:

const resolvePath = (object, path, defaultValue) => path
   .split(/[\.\[\]\'\"]/)
   .filter(p => p)
   .reduce((o, p) => o ? o[p] : defaultValue, object)

例子:

const myVar = {a:{b:[{c:1}]}}
resolvePath(myVar,'a.b[0].c') => 1
resolvePath(myVar,'a["b"][\'0\'].c') => 1

AngularJS有$scope.$eval

在AngularJS中,我们可以使用$scope。$eval方法访问嵌套对象:

$scope.someObject = someObject;
console.log( $scope.$eval("someObject.part3[0].name") ); //Part 3A

有关更多信息,请参见

AngularJS $scope API引用- $eval

演示

angular.module("app",[]) .run(function($rootScope) { $rootScope.someObject = { 'part2' : { 'name': 'Part 2', 'size': '15', 'qty' : '60' }, 'part3' : [{ 'name': 'Part 3A', 'size': '10', 'qty' : '20' },{ name: 'Part 3B' }] }; console.log( "part3[0].name =", $rootScope.$eval("someObject.part3[0].name") ); }) <script src="//unpkg.com/angular/angular.js"></script> <body ng-app="app" </body>

/**
 * Access a deep value inside a object 
 * Works by passing a path like "foo.bar", also works with nested arrays like "foo[0][1].baz"
 * @author Victor B. https://gist.github.com/victornpb/4c7882c1b9d36292308e
 * Unit tests: http://jsfiddle.net/Victornpb/0u1qygrh/
 */
function getDeepVal(obj, path) {
    if (typeof obj === "undefined" || obj === null) return;
    path = path.split(/[\.\[\]\"\']{1,2}/);
    for (var i = 0, l = path.length; i < l; i++) {
        if (path[i] === "") continue;
        obj = obj[path[i]];
        if (typeof obj === "undefined" || obj === null) return;
    }
    return obj;
}

适用于

getDeepVal(obj,'foo.bar')
getDeepVal(obj,'foo.1.bar')
getDeepVal(obj,'foo[0].baz')
getDeepVal(obj,'foo[1][2]')
getDeepVal(obj,"foo['bar'].baz")
getDeepVal(obj,"foo['bar']['baz']")
getDeepVal(obj,"foo.bar.0.baz[1]['2']['w'].aaa[\"f\"].bb")

而不是尝试模拟JS语法,你将不得不花费大量的计算解析,或者只是错误/忘记一些事情,比如一堆这些答案(带.s的键,有人吗?),只是使用一个键数组。

var part1name     = Object.get(someObject, ['part1', 'name']);
var part2quantity = Object.get(someObject, ['part2', 'qty']);
var part3name1    = Object.get(someObject, ['part3', 0, 'name']);

如果您需要使用单个字符串,只需JSONify它。 此方法的另一个改进是您可以删除/设置根级对象。

function resolve(obj, path) {
    let root = obj = [obj];
    path = [0, ...path];
    while (path.length > 1)
        obj = obj[path.shift()];
    return [obj, path[0], root];
}
Object.get = (obj, path) => {
    let [parent, key] = resolve(obj, path);
    return parent[key];
};
Object.del = (obj, path) => {
    let [parent, key, root] = resolve(obj, path);
    delete parent[key];
    return root[0];
};
Object.set = (obj, path, value) => {
    let [parent, key, root] = resolve(obj, path);
    parent[key] = value;
    return root[0];
};

其他功能演示:

对于.set(/.del()的bob =是不必要的,除非你的路径可能是空的(操作根对象)。 我证明我不克隆的对象使用史蒂夫保持原始的引用和检查bob == steve //true后,第一个.set(