使用点符号string访问对象的子属性
我暂时停留在看起来很简单的JavaScript问题上,但也许我只是错过了正确的search关键字!
说我们有一个对象
var r = { a:1, b: {b1:11, b2: 99}};
有几种方法可以访问99:
rbb2 r['b']['b2']
我想要的是能够定义一个string
var s = "b.b2";
然后访问99使用
rs or r[s] //(which of course won't work)
一种方法是写一个函数来分割string上的点,也许recursion/迭代获取属性。 但有没有更简单/更有效的方法? 任何有用的jQuery API在这里?
这是我刚才写的一个简单的函数,但它适用于基本的对象属性:
function getDescendantProp(obj, desc) { var arr = desc.split("."); while(arr.length && (obj = obj[arr.shift()])); return obj; } console.log(getDescendantProp(r, "b.b2")); //-> 99
尽pipe有答案将此扩展为“允许”数组索引访问,但这并不是必须的,因为您可以使用此方法使用点符号指定数字索引:
getDescendantProp({ a: [ 1, 2, 3 ] }, 'a.2'); //-> 3
在传递对象作为initalValue
时分割和减less
var r = { a:1, b: {b1:11, b2: 99}}; var s = "b.b2"; var value = s.split('.').reduce(function(a, b) { return a[b]; }, r); console.log(value);
如果在你的场景中可以将整个数组variables放到一个string中,你可以使用eval()
函数。
var r = { a:1, b: {b1:11, b2: 99}}; var s = "rbb2"; alert(eval(s)); // 99
我可以感觉到人们惊恐万分
你可以使用lodash get()和set()方法 。
得到
var object = { 'a': [{ 'b': { 'c': 3 } }] }; _.get(object, 'a[0].b.c'); // → 3
设置
var object = { 'a': [{ 'b': { 'c': 3 } }] }; _.set(object, 'a[0].b.c', 4); console.log(object.a[0].bc); // → 4
延伸@ JohnB的答案,我也添加了一个setter值。 检查出的PLUNK在
http://plnkr.co/edit/lo0thC?p=preview
function getSetDescendantProp(obj, desc, value) { var arr = desc ? desc.split(".") : []; while (arr.length && obj) { var comp = arr.shift(); var match = new RegExp("(.+)\\[([0-9]*)\\]").exec(comp); // handle arrays if ((match !== null) && (match.length == 3)) { var arrayData = { arrName: match[1], arrIndex: match[2] }; if (obj[arrayData.arrName] !== undefined) { if (typeof value !== 'undefined' && arr.length === 0) { obj[arrayData.arrName][arrayData.arrIndex] = value; } obj = obj[arrayData.arrName][arrayData.arrIndex]; } else { obj = undefined; } continue; } // handle regular things if (typeof value !== 'undefined') { if (obj[comp] === undefined) { obj[comp] = {}; } if (arr.length === 0) { obj[comp] = value; } } obj = obj[comp]; } return obj; }
这是我能做的最简单的事情:
var accessProperties = function(object, string){ var explodedString = string.split('.'); for (i = 0, l = explodedString.length; i<l; i++){ object = object[explodedString[i]]; } return object; } var r = { a:1, b: {b1:11, b2: 99}}; var s = "b.b2"; var o = accessProperties(r, s); alert(o);//99
你也可以做
var s = "['b'].b2"; var num = eval('r'+s);
我不知道支持的jQuery API函数,但我有这个function:
var ret = data; // Your object var childexpr = "b.b2"; // Your expression if (childexpr != '') { var childs = childexpr.split('.'); var i; for (i = 0; i < childs.length && ret != undefined; i++) { ret = ret[childs[i]]; } } return ret;
我已经扩展了Andy E的答案,以便它也可以处理数组:
function getDescendantProp(obj, desc) { var arr = desc.split("."); //while (arr.length && (obj = obj[arr.shift()])); while (arr.length && obj) { var comp = arr.shift(); var match = new RegExp("(.+)\\[([0-9]*)\\]").exec(comp); if ((match !== null) && (match.length == 3)) { var arrayData = { arrName: match[1], arrIndex: match[2] }; if (obj[arrayData.arrName] != undefined) { obj = obj[arrayData.arrName][arrayData.arrIndex]; } else { obj = undefined; } } else { obj = obj[comp] } } return obj; }
有可能更有效的方法来做正则expression式,但它是紧凑的。
你现在可以做这样的东西:
var model = { "m1": { "Id": "22345", "People": [ { "Name": "John", "Numbers": ["07263", "17236", "1223"] }, { "Name": "Jenny", "Numbers": ["2", "3", "6"] }, { "Name": "Bob", "Numbers": ["12", "3333", "4444"] } ] } } // Should give you "6" var x = getDescendantProp(model, "m1.People[1].Numbers[2]");
这里是Andy E的代码的扩展,它返回到数组并返回所有的值:
function GetDescendantProps(target, pathString) { var arr = pathString.split("."); while(arr.length && (target = target[arr.shift()])){ if (arr.length && target.length && target.forEach) { // handle arrays var remainder = arr.join('.'); var results = []; for (var i = 0; i < target.length; i++){ var x = this.GetDescendantProps(target[i], remainder); if (x) results = results.concat(x); } return results; } } return (target) ? [target] : undefined; //single result, wrap in array for consistency }
所以给了这个target
:
var t = {a: {b: [ {'c':'x'}, {'not me':'y'}, {'c':'z'} ] } };
我们得到:
GetDescendantProps(t, "abc") === ["x", "z"]; // true
Andy E,Jason More和我自己的解决scheme的性能testing可以在http://jsperf.com/propertyaccessor上find 。 请随时使用您自己的浏览器运行testing以添加收集的数据。
预后很明显,安迪E的解决scheme是迄今为止最快的!
对于任何感兴趣的人来说,这里是解决原始问题的代码。
function propertyAccessor(object, keys, array) { /* Retrieve an object property with a dot notation string. @param {Object} object Object to access. @param {String} keys Property to access using 0 or more dots for notation. @param {Object} [array] Optional array of non-dot notation strings to use instead of keys. @return {*} */ array = array || keys.split('.') if (array.length > 1) { // recurse by calling self return propertyAccessor(object[array.shift()], null, array) } else { return object[array] } }
简短的回答:不,没有像你想要的本地.access
function。 正如你正确地提到的,你将不得不定义自己的函数来分割string,并对其部分进行循环/检查。
当然,你总是可以做的(即使它认为不好的做法)是使用eval()
。
喜欢
var s = 'b.b2'; eval('r.' + s); // 99
这里有一个更好的方法,然后@安迪的回答,其中obj
(上下文)是可选的 ,如果没有提供,它会回落到window
..
function getDescendantProp(desc, obj) { obj = obj || window; var arr = desc.split("."); while (arr.length && (obj = obj[arr.shift()])); return obj; };