我暂时陷入了一个看似非常简单的JavaScript问题,但也许我只是错过了正确的搜索关键字!
假设我们有一个对象
var r = { a:1, b: {b1:11, b2: 99}};
有几种方法可以访问99:
r.b.b2
r['b']['b2']
我想要的是能够定义一个字符串
var s = "b.b2";
然后使用
访问99r.s or r[s] //(which of course won't work)
一种方法是为它编写一个函数,在dot上拆分字符串,也可以递归/迭代获取属性。但是有更简单/更有效的方法吗?在这里的任何jQuery API中有用吗?
答案 0 :(得分:109)
这是我刚才写的一个天真的函数,但它适用于基本的对象属性:
function getDescendantProp(obj, desc) {
var arr = desc.split(".");
while(arr.length && (obj = obj[arr.shift()]));
return obj;
}
console.log(getDescendantProp(r, "b.b2"));
//-> 99
虽然有些答案会将此扩展为“允许”数组索引访问,但这并不是必需的,因为您可以使用此方法使用点表示法指定数字索引:
getDescendantProp({ a: [ 1, 2, 3 ] }, 'a.2');
//-> 3
答案 1 :(得分:88)
initalValue
传递时,
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);
<强>更新强> (感谢TeChn4K发布的评论)
使用ES6语法,它更短
var r = { a:1, b: {b1:11, b2: 99}};
var s = "b.b2";
var value = s.split('.').reduce((a, b) => a[b], r);
console.log(value);
答案 2 :(得分:22)
您可以使用lodash get() and set() methods。
<强>获取强>
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].b.c);
// → 4
答案 3 :(得分:18)
如果您的场景中可以将整个数组变量放入字符串中,则可以使用eval()
函数。
var r = { a:1, b: {b1:11, b2: 99}};
var s = "r.b.b2";
alert(eval(s)); // 99
我能感觉到人们惊恐万分
答案 4 :(得分:13)
扩展@ JohnB的答案,我也添加了一个setter值。查看
上的plunkerhttp://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;
}
答案 5 :(得分:8)
这是我能做的最简单的事情:
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
答案 6 :(得分:5)
你也可以
var s = "['b'].b2";
var num = eval('r'+s);
答案 7 :(得分:2)
我不知道支持的jQuery API函数,但我有这个函数:
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;
答案 8 :(得分:2)
我已经扩展了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;
}
可能有更有效的方法来制作正则表达式,但它很紧凑。
您现在可以执行以下操作:
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]");
答案 9 :(得分: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, "a.b.c") === ["x", "z"]; // true
答案 10 :(得分:2)
Andy E,Jason More和我自己的解决方案的性能测试可在http://jsperf.com/propertyaccessor获得。请随意使用您自己的浏览器运行测试,以添加到收集的数据中。
预后很明显,Andy E的解决方案是迄今为止最快的!
对于任何有兴趣的人,这是我对原始问题的解决方案的代码。
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]
}
}
答案 11 :(得分:0)
简短回答:不,没有你想要的原生.access
功能。正如您正确提到的那样,您必须定义自己的函数,该函数将字符串和循环/检查分开。
当然,你总能做的事情(即使它被认为是不好的做法)是使用eval()
。
像
var s = 'b.b2';
eval('r.' + s); // 99
答案 12 :(得分:0)
这是一个更好的方式,然后@andy's回答,obj
(上下文)可选,如果没有提供,它会回退到window
。
function getDescendantProp(desc, obj) {
obj = obj || window;
var arr = desc.split(".");
while (arr.length && (obj = obj[arr.shift()]));
return obj;
};