如果我有对象的引用:
var test = {};
可能(但不是立即)有嵌套对象,如:
{level1: {level2: {level3: "level3"}}};
测试最深层嵌套对象中是否存在键的最佳方法是什么?
alert(test.level1);
会产生undefined
,但alert(test.level1.level2.level3);
会失败。
我现在正在做这样的事情:
if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
alert(test.level1.level2.level3);
}
但我想知道是否有更好的方法。
答案 0 :(得分:338)
如果您不想要TypeError
,则必须逐步执行此操作,因为如果其中一个成员是null
或undefined
,并且您尝试访问某个成员将抛出异常。
您可以简单地catch
异常,或者创建一个函数来测试多个级别的存在,如下所示:
function checkNested(obj /*, level1, level2, ... levelN*/) {
var args = Array.prototype.slice.call(arguments, 1);
for (var i = 0; i < args.length; i++) {
if (!obj || !obj.hasOwnProperty(args[i])) {
return false;
}
obj = obj[args[i]];
}
return true;
}
var test = {level1:{level2:{level3:'level3'}} };
checkNested(test, 'level1', 'level2', 'level3'); // true
checkNested(test, 'level1', 'level2', 'foo'); // false
更新2019-05-16:
这是一个较短的版本,使用ES6功能和递归(它也是proper tail call形式):
function checkNested(obj, level, ...rest) {
if (obj === undefined) return false
if (rest.length == 0 && obj.hasOwnProperty(level)) return true
return checkNested(obj[level], ...rest)
}
答案 1 :(得分:324)
这是一种模式picked up from Oliver Steele:
var level3 = (((test || {}).level1 || {}).level2 || {}).level3;
alert( level3 );
事实上,整篇文章讨论了如何在javascript中执行此操作。他习惯于使用上面的语法(一旦你习惯它就不难读)作为一个成语。
答案 2 :(得分:229)
看起来像lodash has added _.get
获取所有需要的嵌套属性。
_.get(countries, 'greece.sparta.playwright')
lodash个用户可能会lodash.contrib享有couple methods that mitigate this problem。
签名: _.getPath(obj:Object, ks:String|Array)
根据描述的路径获取嵌套对象中任何深度的值
给出的钥匙。键可以作为数组或以点分隔的字符串给出。
如果无法到达路径,则返回undefined
。
var countries = {
greece: {
athens: {
playwright: "Sophocles"
}
}
}
};
_.getPath(countries, "greece.athens.playwright");
// => "Sophocles"
_.getPath(countries, "greece.sparta.playwright");
// => undefined
_.getPath(countries, ["greece", "athens", "playwright"]);
// => "Sophocles"
_.getPath(countries, ["greece", "sparta", "playwright"]);
// => undefined
答案 3 :(得分:172)
我已完成performance tests(感谢cdMinix添加lodash)对此问题提出的一些建议以及下面列出的结果。
免责声明#1 将字符串转换为引用是不必要的元编程,最好避免使用。不要忘记你的参考资料。 Read more from this answer to a similar question
免责声明#2 我们在谈论每毫秒数百万次操作。在大多数用例中,这些都不太可能产生太大的影响。了解每个人的局限性,选择哪个最有意义。对我来说,出于方便,我会选择像
reduce
这样的东西。
Object Wrap (by Oliver Steele) - 34% - 最快
var r1 = (((test || {}).level1 || {}).level2 || {}).level3;
var r2 = (((test || {}).level1 || {}).level2 || {}).foo;
Original solution (suggested in question) - 45%
var r1 = test.level1 && test.level1.level2 && test.level1.level2.level3;
var r2 = test.level1 && test.level1.level2 && test.level1.level2.foo;
checkNested - 50%
function checkNested(obj) {
for (var i = 1; i < arguments.length; i++) {
if (!obj.hasOwnProperty(arguments[i])) {
return false;
}
obj = obj[arguments[i]];
}
return true;
}
get_if_exist - 52%
function get_if_exist(str) {
try { return eval(str) }
catch(e) { return undefined }
}
validChain - 54%
function validChain( object, ...keys ) {
return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}
objHasKeys - 63%
function objHasKeys(obj, keys) {
var next = keys.shift();
return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}
nestedPropertyExists - 69%
function nestedPropertyExists(obj, props) {
var prop = props.shift();
return prop === undefined ? true : obj.hasOwnProperty(prop) ? nestedPropertyExists(obj[prop], props) : false;
}
_.get - 72%
deeptest - 86%
function deeptest(target, s){
s= s.split('.')
var obj= target[s.shift()];
while(obj && s.length) obj= obj[s.shift()];
return obj;
}
sad clowns - 100% - 最慢
var o = function(obj) { return obj || {} };
var r1 = o(o(o(o(test).level1).level2).level3);
var r2 = o(o(o(o(test).level1).level2).foo);
答案 4 :(得分:41)
如果您像字符串一样处理名称,则可以读取任何深度的对象属性:'t.level1.level2.level3'
。
window.t={level1:{level2:{level3: 'level3'}}};
function deeptest(s){
s= s.split('.')
var obj= window[s.shift()];
while(obj && s.length) obj= obj[s.shift()];
return obj;
}
alert(deeptest('t.level1.level2.level3') || 'Undefined');
如果任何细分受众群为undefined
,则会返回undefined
。
答案 5 :(得分:25)
var a;
a = {
b: {
c: 'd'
}
};
function isset (fn) {
var value;
try {
value = fn();
} catch (e) {
value = undefined;
} finally {
return value !== undefined;
}
};
// ES5
console.log(
isset(function () { return a.b.c; }),
isset(function () { return a.b.c.d.e.f; })
);
如果您在ES6环境中编码(或使用6to5),那么您可以利用arrow function语法:
// ES6 using the arrow function
console.log(
isset(() => a.b.c),
isset(() => a.b.c.d.e.f)
);
关于性能,如果设置了属性,则使用try..catch
块不会有性能损失。如果未设置该属性,则会对性能产生影响。
只考虑使用_.has
:
var object = { 'a': { 'b': { 'c': 3 } } };
_.has(object, 'a');
// → true
_.has(object, 'a.b.c');
// → true
_.has(object, ['a', 'b', 'c']);
// → true
答案 6 :(得分:18)
怎么样
try {
alert(test.level1.level2.level3)
} catch(e) {
...whatever
}
答案 7 :(得分:13)
const propExists = (obj, path) => {
return !!path.split('.').reduce((obj, prop) => {
return obj && obj[prop] ? obj[prop] : undefined;
}, obj)
}
答案 8 :(得分:10)
您还可以将tc39可选链接提议与babel 7 - tc39-proposal-optional-chaining
一起使用代码看起来像这样:
const test = test?.level1?.level2?.level3;
if (test) alert(test);
答案 9 :(得分:8)
我尝试了一种递归方法:
function objHasKeys(obj, keys) {
var next = keys.shift();
return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}
! keys.length ||
从递归开始,因此它不会运行该函数而没有剩余的密钥进行测试。测试:
obj = {
path: {
to: {
the: {
goodKey: "hello"
}
}
}
}
console.log(objHasKeys(obj, ['path', 'to', 'the', 'goodKey'])); // true
console.log(objHasKeys(obj, ['path', 'to', 'the', 'badKey'])); // undefined
我用它来打印一堆具有未知键/值的对象的友好html视图,例如:
var biosName = objHasKeys(myObj, 'MachineInfo:BiosInfo:Name'.split(':'))
? myObj.MachineInfo.BiosInfo.Name
: 'unknown';
答案 10 :(得分:7)
我认为以下脚本提供了更具可读性的表示。
声明一个函数:
var o = function(obj) { return obj || {};};
然后像这样使用它:
if (o(o(o(o(test).level1).level2).level3)
{
}
我称之为&#34;悲伤的小丑技巧&#34;因为它正在使用符号o(
编辑:
这是 TypeScript
的版本它在编译时提供类型检查(如果你使用像Visual Studio这样的工具,则提供intellisense)
export function o<T>(someObject: T, defaultValue: T = {} as T) : T {
if (typeof someObject === 'undefined' || someObject === null)
return defaultValue;
else
return someObject;
}
用法相同:
o(o(o(o(test).level1).level2).level3
但是这次intellisense有效!
加上,您可以设置默认值:
o(o(o(o(o(test).level1).level2).level3, "none")
答案 11 :(得分:5)
我没有看到任何使用Proxies
的人的例子所以我想出了自己的。
关于它的好处是你不必插入字符串。你实际上可以返回一个可链的 object 函数,并用它做一些神奇的事情。您甚至可以调用函数并获取数组索引以检查深层对象
function resolve(target) {
var noop = () => {} // We us a noop function so we can call methods also
return new Proxy(noop, {
get(noop, key) {
// return end result if key is _result
return key === '_result'
? target
: resolve( // resolve with target value or undefined
target === undefined ? undefined : target[key]
)
},
// if we want to test a function then we can do so alos thanks to using noop
// instead of using target in our proxy
apply(noop, that, args) {
return resolve(typeof target === 'function' ? target.apply(that, args) : undefined)
},
})
}
// some modified examples from the accepted answer
var test = {level1: {level2:() => ({level3:'level3'})}}
var test1 = {key1: {key2: ['item0']}}
// You need to get _result in the end to get the final result
console.log(resolve(test).level1.level2().level3._result)
console.log(resolve(test).level1.level2().level3.level4.level5._result)
console.log(resolve(test1).key1.key2[0]._result)
console.log(resolve(test1)[0].key._result) // don't exist
以上代码适用于同步内容。但是你会如何测试像ajax调用那样异步的东西呢? 你是如何测试的?如果响应不是json时返回500 http错误怎么办?
window.fetch('https://httpbin.org/get')
.then(function(response) {
return response.json()
})
.then(function(json) {
console.log(json.headers['User-Agent'])
})
确定你可以使用async / await来摆脱一些回调。但是,如果你能做得更神奇呢?看起来像这样的东西:
fetch('https://httpbin.org/get').json().headers['User-Agent']
你可能想知道所有的承诺和.then
链是......这可能会阻塞所有你知道的...但是使用相同的代理技术和承诺你可以实际测试深层嵌套的复杂路径,而不需要编写单个函数
function resolve(target) {
return new Proxy(() => {}, {
get(noop, key) {
return key === 'then' ? target.then.bind(target) : resolve(
Promise.resolve(target).then(target => {
if (typeof target[key] === 'function') return target[key].bind(target)
return target[key]
})
)
},
apply(noop, that, args) {
return resolve(target.then(result => {
return result.apply(that, args)
}))
},
})
}
// this feels very much synchronous but are still non blocking :)
resolve(window) // this will chain a noop function until you call then()
.fetch('https://httpbin.org/get')
.json()
.headers['User-Agent']
.then(console.log, console.warn) // you get a warning if it doesn't exist
// You could use this method also for the first test object
// also, but it would have to call .then() in the end
// Another example
resolve(window)
.fetch('https://httpbin.org/get?items=4&items=2')
.json()
.args
.items
// nice that you can map an array item without even having it ready
.map(n => ~~n * 4)
.then(console.log, console.warn) // you get a warning if it doesn't exist
答案 12 :(得分:4)
基于this answer,我使用ES2015
提出了这个通用函数来解决问题
function validChain( object, ...keys ) {
return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}
var test = {
first: {
second: {
third: "This is not the key your are looking for"
}
}
}
if ( validChain( test, "first", "second", "third" ) ) {
console.log( test.first.second.third );
}
答案 13 :(得分:4)
一个简单的方法是:
try {
alert(test.level1.level2.level3);
} catch(e) {
alert("undefined"); // this is optional to put any output here
}
当try/catch
未定义任何更高级别的对象(如test,test.level1,test.level1.level2)时,会捕获这些情况。
答案 14 :(得分:3)
我创建了一个小函数来安全地获取嵌套对象的属性。
function getValue(object, path, fallback, fallbackOnFalsy) {
if (!object || !path) {
return fallback;
}
// Reduces object properties to the deepest property in the path argument.
return path.split('.').reduce((object, property) => {
if (object && typeof object !== 'string' && object.hasOwnProperty(property)) {
// The property is found but it may be falsy.
// If fallback is active for falsy values, the fallback is returned, otherwise the property value.
return !object[property] && fallbackOnFalsy ? fallback : object[property];
} else {
// Returns the fallback if current chain link does not exist or it does not contain the property.
return fallback;
}
}, object);
}
或更简单但不太可读的版本:
function getValue(o, path, fb, fbFalsy) {
if(!o || !path) return fb;
return path.split('.').reduce((o, p) => o && typeof o !== 'string' && o.hasOwnProperty(p) ? !o[p] && fbFalsy ? fb : o[p] : fb, o);
}
或更短,但不会在虚假标志上回退:
function getValue(o, path, fb) {
if(!o || !path) return fb;
return path.split('.').reduce((o, p) => o && typeof o !== 'string' && o.hasOwnProperty(p) ? o[p] : fb, o);
}
我通过以下方式进行测试:
const obj = {
c: {
a: 2,
b: {
c: [1, 2, 3, {a: 15, b: 10}, 15]
},
c: undefined,
d: null
},
d: ''
}
这是一些测试:
// null
console.log(getValue(obj, 'c.d', 'fallback'));
// array
console.log(getValue(obj, 'c.b.c', 'fallback'));
// array index 2
console.log(getValue(obj, 'c.b.c.2', 'fallback'));
// no index => fallback
console.log(getValue(obj, 'c.b.c.10', 'fallback'));
答案 15 :(得分:3)
@ CMS的简短ES5版本的优秀答案:
// Check the obj has the keys in the order mentioned. Used for checking JSON results.
var checkObjHasKeys = function(obj, keys) {
var success = true;
keys.forEach( function(key) {
if ( ! obj.hasOwnProperty(key)) {
success = false;
}
obj = obj[key];
})
return success;
}
进行类似的测试:
var test = { level1:{level2:{level3:'result'}}};
utils.checkObjHasKeys(test, ['level1', 'level2', 'level3']); // true
utils.checkObjHasKeys(test, ['level1', 'level2', 'foo']); // false
答案 16 :(得分:2)
我知道这个问题已经过时了,但我想通过将其添加到所有对象来提供扩展。我知道人们倾向于使用Object原型来扩展对象功能,但我发现没有比这更容易的了。此外,现在允许使用Object.defineProperty方法。
Object.defineProperty( Object.prototype, "has", { value: function( needle ) {
var obj = this;
var needles = needle.split( "." );
for( var i = 0; i<needles.length; i++ ) {
if( !obj.hasOwnProperty(needles[i])) {
return false;
}
obj = obj[needles[i]];
}
return true;
}});
现在,为了测试任何对象中的任何属性,您只需执行以下操作:
if( obj.has("some.deep.nested.object.somewhere") )
Here's a jsfiddle来测试它,特别是它包含一些jQuery,如果你直接修改Object.prototype会因为属性变得可枚举而中断。这应该适用于第三方库。
答案 17 :(得分:2)
CMS提供的答案可以正常使用以下修改进行空检查
function checkNested(obj /*, level1, level2, ... levelN*/)
{
var args = Array.prototype.slice.call(arguments),
obj = args.shift();
for (var i = 0; i < args.length; i++)
{
if (obj == null || !obj.hasOwnProperty(args[i]) )
{
return false;
}
obj = obj[args[i]];
}
return true;
}
答案 18 :(得分:2)
适用于所有对象和数组:)
前:
if( obj._has( "something.['deep']['under'][1][0].item" ) ) {
//do something
}
这是我改进版的Brian的回答
我使用 _has 作为属性名称,因为它可能与现有的has属性冲突(例如:maps)
Object.defineProperty( Object.prototype, "_has", { value: function( needle ) {
var obj = this;
var needles = needle.split( "." );
var needles_full=[];
var needles_square;
for( var i = 0; i<needles.length; i++ ) {
needles_square = needles[i].split( "[" );
if(needles_square.length>1){
for( var j = 0; j<needles_square.length; j++ ) {
if(needles_square[j].length){
needles_full.push(needles_square[j]);
}
}
}else{
needles_full.push(needles[i]);
}
}
for( var i = 0; i<needles_full.length; i++ ) {
var res = needles_full[i].match(/^((\d+)|"(.+)"|'(.+)')\]$/);
if (res != null) {
for (var j = 0; j < res.length; j++) {
if (res[j] != undefined) {
needles_full[i] = res[j];
}
}
}
if( typeof obj[needles_full[i]]=='undefined') {
return false;
}
obj = obj[needles_full[i]];
}
return true;
}});
这里是fiddle
答案 19 :(得分:2)
这是我的看法 - 大多数这些解决方案忽略了嵌套数组的情况,如:
obj = {
"l1":"something",
"l2":[{k:0},{k:1}],
"l3":{
"subL":"hello"
}
}
我可能想查看obj.l2[0].k
使用以下功能,您可以执行deeptest('l2[0].k',obj)
如果对象存在,函数将返回true,否则返回
function deeptest(keyPath, testObj) {
var obj;
keyPath = keyPath.split('.')
var cKey = keyPath.shift();
function get(pObj, pKey) {
var bracketStart, bracketEnd, o;
bracketStart = pKey.indexOf("[");
if (bracketStart > -1) { //check for nested arrays
bracketEnd = pKey.indexOf("]");
var arrIndex = pKey.substr(bracketStart + 1, bracketEnd - bracketStart - 1);
pKey = pKey.substr(0, bracketStart);
var n = pObj[pKey];
o = n? n[arrIndex] : undefined;
} else {
o = pObj[pKey];
}
return o;
}
obj = get(testObj, cKey);
while (obj && keyPath.length) {
obj = get(obj, keyPath.shift());
}
return typeof(obj) !== 'undefined';
}
var obj = {
"l1":"level1",
"arr1":[
{"k":0},
{"k":1},
{"k":2}
],
"sub": {
"a":"letter A",
"b":"letter B"
}
};
console.log("l1: " + deeptest("l1",obj));
console.log("arr1[0]: " + deeptest("arr1[0]",obj));
console.log("arr1[1].k: " + deeptest("arr1[1].k",obj));
console.log("arr1[1].j: " + deeptest("arr1[1].j",obj));
console.log("arr1[3]: " + deeptest("arr1[3]",obj));
console.log("arr2: " + deeptest("arr2",obj));
答案 20 :(得分:2)
如果属性存在,我正在寻找要返回的值,所以我在上面用CMS修改了答案。这就是我想出的:
function getNestedProperty(obj, key) {
// Get property array from key string
var properties = key.split(".");
// Iterate through properties, returning undefined if object is null or property doesn't exist
for (var i = 0; i < properties.length; i++) {
if (!obj || !obj.hasOwnProperty(properties[i])) {
return;
}
obj = obj[properties[i]];
}
// Nested property found, so return the value
return obj;
}
Usage:
getNestedProperty(test, "level1.level2.level3") // "level3"
getNestedProperty(test, "level1.level2.foo") // undefined
答案 21 :(得分:2)
我认为这是一个小小的进步(成为一线):
alert( test.level1 && test.level1.level2 && test.level1.level2.level3 )
这是因为和&amp;&amp; operator返回它评估的最终操作数(并且它会短路)。
答案 22 :(得分:2)
从this answer开始详细阐述了以下选项。两者都是同一棵树:
var o = { a: { b: { c: 1 } } };
var u = undefined;
o.a ? o.a.b ? o.a.b.c : u : u // 1
o.x ? o.x.y ? o.x.y.z : u : u // undefined
(o = o.a) ? (o = o.b) ? o.c : u : u // 1
var $ = function (empty) {
return function (node) {
return node || empty;
};
}({});
$($(o.a).b).c // 1
$($(o.x).y).z // undefined
答案 23 :(得分:2)
现在我们也可以使用reduce
循环嵌套键:
// @params o<object>
// @params path<string> expects 'obj.prop1.prop2.prop3'
// returns: obj[path] value or 'false' if prop doesn't exist
const objPropIfExists = o => path => {
const levels = path.split('.');
const res = (levels.length > 0)
? levels.reduce((a, c) => a[c] || 0, o)
: o[path];
return (!!res) ? res : false
}
const obj = {
name: 'Name',
sys: { country: 'AU' },
main: { temp: '34', temp_min: '13' },
visibility: '35%'
}
const exists = objPropIfExists(obj)('main.temp')
const doesntExist = objPropIfExists(obj)('main.temp.foo.bar.baz')
console.log(exists, doesntExist)
&#13;
答案 24 :(得分:2)
您可以使用递归函数执行此操作。即使您不知道所有嵌套的对象键名称,这也会起作用。
function FetchKeys(obj) {
let objKeys = [];
let keyValues = Object.entries(obj);
for (let i in keyValues) {
objKeys.push(keyValues[i][0]);
if (typeof keyValues[i][1] == "object") {
var keys = FetchKeys(keyValues[i][1])
objKeys = objKeys.concat(keys);
}
}
return objKeys;
}
let test = { level1: { level2: { level3: "level3" } } };
let keyToCheck = "level2";
let keys = FetchKeys(test); //Will return an array of Keys
if (keys.indexOf(keyToCheck) != -1) {
//Key Exists logic;
}
else {
//Key Not Found logic;
}
答案 25 :(得分:1)
好吧,单行在html模板中使用并没有很好的答案,所以我使用 ES6代理制作了一个。 您只需将一个对象或值传递给“遍历”函数,然后进行多次嵌套调用即可,这些嵌套调用可以通过返回值或后备值的函数调用来关闭。 使用:
const testObject = {
deep: {
nested: {
obj: {
closure: () => { return "closure" },
number: 9,
boolean: true,
array: [1, 2, { foo: { bar: true } }]
}
}
}
}
traverse(testObject).deep()
// {nested: {…}}
traverse(testObject).non.existent()
// undefined
traverse(testObject).deep.nested.obj.closure()()
// closure
traverse(testObject).deep.nested.obj.array[5]('fallback')
// fallback
traverse(testObject).deep.nested.obj.array[2]()
// {foo: {…}}
traverse(testObject).deep.nested.obj.array[2].foo.bar()
// true
traverse(testObject).deep.nested.obj.array[2].foo.bar[4]('fallback')
// fallback
traverse(testObject).completely.wrong[3].call().WILL_THROW()
// Uncaught TypeError: Cannot read property 'WILL_THROW' of undefined
功能本身:
const traverse = (input) => {
// unique empty object
const unset = new Object();
// we need wrapper to ensure we have access to the same unique empty object
const closure = (input) => {
// wrap each input into this
const handler = new Function();
handler.input = input;
// return wrappers proxy
return new Proxy(handler, {
// keep traversing
get: (target, name) => {
// if undefined supplied as initial input
if (!target.input) {
return closure(unset);
}
// otherwise
if (target.input[name] !== undefined) {
// input has that property
return closure(target.input[name]);
} else {
return closure(unset);
}
},
// result with fallback
apply: (target, context, args) => {
return handler.input === unset ?
args[0] : handler.input;
}
})
}
return closure(input);
}
答案 26 :(得分:1)
轻微编辑this answer以允许路径中的嵌套数组
var has = function (obj, key) {
return key.split(".").every(function (x) {
if (typeof obj != "object" || obj === null || !x in obj)
return false;
if (obj.constructor === Array)
obj = obj[0];
obj = obj[x];
return true;
});
}
&#13;
检查用法的链接答案:)
答案 27 :(得分:1)
我想我又添加了另一个我今天想出来的。我为这个解决方案感到自豪的原因是它避免了许多解决方案中使用的嵌套括号,例如Object Wrap (by Oliver Steele):
(在本例中,我使用下划线作为占位符变量,但任何变量名称都可以使用)
//the 'test' object
var test = {level1: {level2: {level3: 'level3'}}};
let _ = test;
if ((_=_.level1) && (_=_.level2) && (_=_.level3)) {
let level3 = _;
//do stuff with level3
}
&#13;
//you could also use 'stacked' if statements. This helps if your object goes very deep.
//(formatted without nesting or curly braces except the last one)
let _ = test;
if (_=_.level1)
if (_=_.level2)
if (_=_.level3) {
let level3 = _;
//do stuff with level3
}
//or you can indent:
if (_=_.level1)
if (_=_.level2)
if (_=_.level3) {
let level3 = _;
//do stuff with level3
}
&#13;
答案 28 :(得分:1)
尝试
function isExist(arg){
try{
return arg();
}catch(e){
return false;
}
}
let obj={a:5,b:{c:5}};
console.log(isExist(()=>obj.b.c))
console.log(isExist(()=>obj.b.foo))
console.log(isExist(()=>obj.test.foo))
答案 29 :(得分:1)
只需使用 https://www.npmjs.com/package/js-aid 包来检查嵌套对象。
答案 30 :(得分:1)
我遇到了同样的问题,并希望看看我是否可以提出自己的解决方案。这会接受您要检查为字符串的路径。
function checkPathForTruthy(obj, path) {
if (/\[[a-zA-Z_]/.test(path)) {
console.log("Cannot resolve variables in property accessors");
return false;
}
path = path.replace(/\[/g, ".");
path = path.replace(/]|'|"/g, "");
path = path.split(".");
var steps = 0;
var lastRef = obj;
var exists = path.every(key => {
var currentItem = lastRef[path[steps]];
if (currentItem) {
lastRef = currentItem;
steps++;
return true;
} else {
return false;
}
});
return exists;
}
这是一个包含一些日志记录和测试用例的代码段:
console.clear();
var testCases = [
["data.Messages[0].Code", true],
["data.Messages[1].Code", true],
["data.Messages[0]['Code']", true],
['data.Messages[0]["Code"]', true],
["data[Messages][0]['Code']", false],
["data['Messages'][0]['Code']", true]
];
var path = "data.Messages[0].Code";
var obj = {
data: {
Messages: [{
Code: "0"
}, {
Code: "1"
}]
}
}
function checkPathForTruthy(obj, path) {
if (/\[[a-zA-Z_]/.test(path)) {
console.log("Cannot resolve variables in property accessors");
return false;
}
path = path.replace(/\[/g, ".");
path = path.replace(/]|'|"/g, "");
path = path.split(".");
var steps = 0;
var lastRef = obj;
var logOutput = [];
var exists = path.every(key => {
var currentItem = lastRef[path[steps]];
if (currentItem) {
logOutput.push(currentItem);
lastRef = currentItem;
steps++;
return true;
} else {
return false;
}
});
console.log(exists, logOutput);
return exists;
}
testCases.forEach(testCase => {
if (checkPathForTruthy(obj, testCase[0]) === testCase[1]) {
console.log("Passed: " + testCase[0]);
} else {
console.log("Failed: " + testCase[0] + " expected " + testCase[1]);
}
});
答案 31 :(得分:1)
//Just in case is not supported or not included by your framework
//***************************************************
Array.prototype.some = function(fn, thisObj) {
var scope = thisObj || window;
for ( var i=0, j=this.length; i < j; ++i ) {
if ( fn.call(scope, this[i], i, this) ) {
return true;
}
}
return false;
};
//****************************************************
function isSet (object, string) {
if (!object) return false;
var childs = string.split('.');
if (childs.length > 0 ) {
return !childs.some(function (item) {
if (item in object) {
object = object[item];
return false;
} else return true;
});
} else if (string in object) {
return true;
} else return false;
}
var object = {
data: {
item: {
sub_item: {
bla: {
here : {
iam: true
}
}
}
}
}
};
console.log(isSet(object,'data.item')); // true
console.log(isSet(object,'x')); // false
console.log(isSet(object,'data.sub_item')); // false
console.log(isSet(object,'data.item')); // true
console.log(isSet(object,'data.item.sub_item.bla.here.iam')); // true
答案 32 :(得分:1)
我编写了自己的函数,它采用了所需的路径,并且具有好的和坏的回调函数。
function checkForPathInObject(object, path, callbackGood, callbackBad){
var pathParts = path.split(".");
var currentObjectPath = object;
// Test every step to see if it exists in object
for(var i=0; i<(pathParts.length); i++){
var currentPathPart = pathParts[i];
if(!currentObjectPath.hasOwnProperty(pathParts[i])){
if(callbackBad){
callbackBad();
}
return false;
} else {
currentObjectPath = currentObjectPath[pathParts[i]];
}
}
// call full path in callback
callbackGood();
}
用法:
var testObject = {
level1:{
level2:{
level3:{
}
}
}
};
checkForPathInObject(testObject, "level1.level2.level3", function(){alert("good!")}, function(){alert("bad!")}); // good
checkForPathInObject(testObject, "level1.level2.level3.levelNotThere", function(){alert("good!")}, function(){alert("bad!")}); //bad
答案 33 :(得分:1)
有一个函数here on thecodeabode (safeRead),它将以安全的方式执行此操作...即
safeRead(test, 'level1', 'level2', 'level3');
如果任何属性为null或未定义,则返回空字符串
答案 34 :(得分:1)
基于a previous comment,这是另一个无法定义主要对象的版本:
// Supposing that our property is at first.second.third.property:
var property = (((typeof first !== 'undefined' ? first : {}).second || {}).third || {}).property;
答案 35 :(得分:0)
getValue (o, key1, key2, key3, key4, key5) {
try {
return o[key1][key2][key3][key4][key5]
} catch (e) {
return null
}
}
答案 36 :(得分:0)
这个功能怎么样?它不需要单独列出每个嵌套属性,而是维护“点”语法(尽管在字符串中)使其更具可读性。
如果未找到该属性,则返回 undefined
或指定的默认值,如果找到则返回该属性的值。
val(obj, element, default_value)
// Recursively checks whether a property of an object exists. Supports multiple-level nested properties separated with '.' characters.
// obj = the object to test
// element = (string or array) the name of the element to test for. To test for a multi-level nested property, separate properties with '.' characters or pass as array)
// default_value = optional default value to return if the item is not found. Returns undefined if no default_value is specified.
// Returns the element if it exists, or undefined or optional default_value if not found.
// Examples: val(obj1, 'prop1.subprop1.subsubprop2');
// val(obj2, 'p.r.o.p', 'default_value');
{
// If no element is being requested, return obj. (ends recursion - exists)
if (!element || element.length == 0) { return obj; }
// if the element isn't an object, then it can't have properties. (ends recursion - does not exist)
if (typeof obj != 'object') { return default_value; }
// Convert element to array.
if (typeof element == 'string') { element = element.split('.') }; // Split on dot (.)
// Recurse into the list of nested properties:
let first = element.shift();
return val(obj[first], element, default_value);
}
答案 37 :(得分:0)
有一个小的模式,但是有时会变得不知所措。我建议您一次使用两个或三个嵌套。
'id'
正如我可能忘记提及的那样,您也可以进一步扩展。下面的示例显示了对嵌套import groovy.transform.TypeChecked
@TypeChecked
class SomeGroovyClass {
Optional<String> someGroovyMethod() {
return Optional.empty()
}
}
的检查,如果没有可用的检查,则将返回。
if (!(foo.bar || {}).weep) return;
// Return if there isn't a 'foo.bar' or 'foo.bar.weep'.
答案 38 :(得分:0)
如果碰巧正在使用AngularJ,则可以使用 $ parse 服务来检查是否存在深对象属性,如下所示:
if( $parse('model.data.items')(vm) ) {
vm.model.data.items.push('whatever');
}
避免这样的语句:
if(vm.model && vm.model.data && vm.model.data.items) {
....
}
不要忘记将 $ parse 服务注入到您的控制器中
答案 39 :(得分:0)
还有一个非常紧凑的
function ifSet(object, path) {
return path.split('.').reduce((obj, part) => obj && obj[part], object)
}
称为:
let a = {b:{c:{d:{e:'found!'}}}}
ifSet(a, 'b.c.d.e') == 'found!'
ifSet(a, 'a.a.a.a.a.a') == undefined
由于它会拆分字符串(但会提高调用的可读性)并遍历所有内容,因此效果不佳,即使已经很明显找不到任何内容(但会提高函数本身的可读性)。
至少比_.get
http://jsben.ch/aAtmc
答案 40 :(得分:0)
/**
* @method getValue
* @description simplifies checking for existance and getting a deeply nested value within a ceratin context
* @argument {string} s string representation of the full path to the requested property
* @argument {object} context optional - the context to check defaults to window
* @returns the value if valid and set, returns undefined if invalid / not available etc.
*/
var getValue = function( s, context ){
var fn = function(){
try{
return eval(s);
}catch(e){
return undefined;
}
}
return fn.call(context||window,s);
}
和用法:
if( getValue('a[0].b[0].b[0].d') == 2 ) // true
答案 41 :(得分:0)
很多答案,但仍然:为什么不更简单?
获取该值的es5版本为:
function value(obj, keys) {
if (obj === undefined) return obj;
if (keys.length === 1 && obj.hasOwnProperty(keys[0])) return obj[keys[0]];
return value(obj[keys.shift()], keys);
}
if (value(test, ['level1', 'level2', 'level3'])) {
// do something
}
您还可以将其与value(config, ['applet', i, 'height']) || 42
他的ES6解决方案向CMS表示感谢,使我有了这个主意。
答案 42 :(得分:0)
另一个ES5解决方案:
function hasProperties(object, properties) {
return !properties.some(function(property){
if (!object.hasOwnProperty(property)) {
return true;
}
object = object[property];
return false;
});
}
答案 43 :(得分:0)
function propsExists(arg) {
try {
const result = arg()
if (typeof result !== 'undefined') {
return true
}
return false
} catch (e) {
return false;
}
}
此功能还将测试0
,null
。
如果存在,它们还将返回true
。
示例:
function propsExists(arg) {
try {
const result = arg()
if (typeof result !== 'undefined') {
return true
}
return false
} catch (e) {
return false;
}
}
let obj = {
test: {
a: null,
b: 0,
c: undefined,
d: 4,
e: 'Hey',
f: () => {},
g: 5.4,
h: false,
i: true,
j: {},
k: [],
l: {
a: 1,
}
}
};
console.log('obj.test.a', propsExists(() => obj.test.a))
console.log('obj.test.b', propsExists(() => obj.test.b))
console.log('obj.test.c', propsExists(() => obj.test.c))
console.log('obj.test.d', propsExists(() => obj.test.d))
console.log('obj.test.e', propsExists(() => obj.test.e))
console.log('obj.test.f', propsExists(() => obj.test.f))
console.log('obj.test.g', propsExists(() => obj.test.g))
console.log('obj.test.h', propsExists(() => obj.test.h))
console.log('obj.test.i', propsExists(() => obj.test.i))
console.log('obj.test.j', propsExists(() => obj.test.j))
console.log('obj.test.k', propsExists(() => obj.test.k))
console.log('obj.test.l', propsExists(() => obj.test.l))
答案 44 :(得分:0)
我已使用此函数访问深度嵌套对象的属性,并且对我有用...
这是功能
ledger_effective_time
这就是我访问深度嵌套对象属性的方式
/**
* get property of object
* @param obj object
* @param path e.g user.name
*/
getProperty(obj, path, defaultValue = '-') {
const value = path.split('.').reduce((o, p) => o && o[p], obj);
return value ? value : defaultValue;
}
答案 45 :(得分:0)
另一种方式:
/**
* This API will return particular object value from JSON Object hierarchy.
*
* @param jsonData : json type : JSON data from which we want to get particular object
* @param objHierarchy : string type : Hierarchical representation of object we want to get,
* For example, 'jsonData.Envelope.Body["return"].patient' OR 'jsonData.Envelope.return.patient'
* Minimal Requirements : 'X.Y' required.
* @returns evaluated value of objHierarchy from jsonData passed.
*/
function evalJSONData(jsonData, objHierarchy){
if(!jsonData || !objHierarchy){
return null;
}
if(objHierarchy.indexOf('["return"]') !== -1){
objHierarchy = objHierarchy.replace('["return"]','.return');
}
let objArray = objHierarchy.split(".");
if(objArray.length === 2){
return jsonData[objArray[1]];
}
return evalJSONData(jsonData[objArray[1]], objHierarchy.substring(objHierarchy.indexOf(".")+1));
}
答案 46 :(得分:0)
我自动化了这个过程
if(isset(object,["prop1","prop2"])){
// YES!
}
function isset(object, props){
var dump;
try {
for(var x in props){
if(x == 0) {
dump = object[props[x]];
return;
}
dump = dump[props[x]];
}
} catch(e) {
return false;
}
return true;
}
答案 47 :(得分:0)
function getValue(base, strValue) {
if(base == null) return;
let currentKey = base;
const keys = strValue.split(".");
let parts;
for(let i=1; i < keys.length; i++) {
parts = keys[i].split("[");
if(parts == null || parts[0] == null) return;
let idx;
if(parts.length > 1) { // if array
idx = parseInt(parts[1].split("]")[0]);
currentKey = currentKey[parts[0]][idx];
} else {
currentKey = currentKey[parts[0]];
}
if(currentKey == null) return;
}
return currentKey;
}
调用函数返回未定义,如果结果在嵌套或值本身的任何地方失败
const a = {
b: {
c: [
{
d: 25
}
]
}
}
console.log(getValue(a, 'a.b.c[1].d'))
// output
25
答案 48 :(得分:0)
基于@Stephane LaFlèche's answer,我提出了我的替代版本的脚本。
var obj = {"a":{"b":{"c":"Hello World"}},"resTest":"potato","success":"This path exists"};
checkForPathInObject = function(object,path,value) {
var pathParts = path.split("."),
result = false;
// Check if required parameters are set; if not, return false
if(!object || typeof object == 'undefined' || !path || typeof path != 'string')
return false;
/* Loop through object keys to find a way to the path or check for value
* If the property does not exist, set result to false
* If the property is an object, update @object
* Otherwise, update result */
for(var i=0;i<pathParts.length;i++){
var currentPathPart = pathParts[i];
if(!object.hasOwnProperty( currentPathPart )) {
result = false;
} else if (object[ currentPathPart ] && path == pathParts[i]) {
result = pathParts[i];
break;
} else if(typeof object[ currentPathPart ] == 'object') {
object = object[ currentPathPart ];
} else {
result = object[ currentPathPart ];
}
}
/* */
if(typeof value != 'undefined' && value == result)
return true;
return result;
};
// Uncomment the lines below to test the script
// alert( checkForPathInObject(obj,'a.b.c') ); // Results "Hello World"
// alert( checkForPathInObject(obj,'a.success') ); // Returns false
// alert( checkForPathInObject(obj,'resTest', 'potato') ); // Returns true
答案 49 :(得分:0)
这里有一个小辅助功能,对我来说,它非常简单明了。希望对某些人有帮助:)。
static issetFromIndices(param, indices, throwException = false) {
var temp = param;
try {
if (!param) {
throw "Parameter is null.";
}
if(!Array.isArray(indices)) {
throw "Indices parameter must be an array.";
}
for (var i = 0; i < indices.length; i++) {
var index = indices[i];
if (typeof temp[index] === "undefined") {
throw "'" + index + "' index is undefined.";
}
temp = temp[index];
}
} catch (e) {
if (throwException) {
throw new Error(e);
} else {
return false;
}
}
return temp;
}
var person = {
hobbies: {
guitar: {
type: "electric"
}
}
};
var indices = ["hobbies", "guitar", "type"];
var throwException = true;
try {
var hobbyGuitarType = issetFromIndices(person, indices, throwException);
console.log("Yay, found index: " + hobbyGuitarType);
} catch(e) {
console.log(e);
}
答案 50 :(得分:0)
您可以使用“。”分隔对象和路径。
function checkPathExist(obj, path) {
var pathArray =path.split(".")
for (var i of pathArray) {
if (Reflect.get(obj, i)) {
obj = obj[i];
}else{
return false;
}
}
return true;
}
var test = {level1:{level2:{level3:'level3'}} };
console.log('level1.level2.level3 => ',checkPathExist(test, 'level1.level2.level3')); // true
console.log( 'level1.level2.foo => ',checkPathExist(test, 'level1.level2.foo')); // false
答案 51 :(得分:0)
我长时间使用的解决方案(不常使用字符串,找不到更好)
function get_if_exist(str){
try{return eval(str)}
catch(e){return undefined}
}
// way to use
if(get_if_exist('test.level1.level2.level3')) {
alert(test.level1.level2.level3);
}
// or simply
alert(get_if_exist('test.level1.level2.level3'));
编辑:仅当对象“test”具有全局范围/范围时才能工作。 否则你必须做类似的事情:
// i think it's the most beautiful code I have ever write :p
function get_if_exist(obj){
return arguments.length==1 || (obj[arguments[1]] && get_if_exist.apply(this,[obj[arguments[1]]].concat([].slice.call(arguments,2))));
}
alert(get_if_exist(test,'level1','level2','level3'));
编辑最终版本以允许2种调用方法:
function get_if_exist(obj){
var a=arguments, b=a.callee; // replace a.callee by the function name you choose because callee is depreceate, in this case : get_if_exist
// version 1 calling the version 2
if(a[1] && ~a[1].indexOf('.'))
return b.apply(this,[obj].concat(a[1].split('.')));
// version 2
return a.length==1 ? a[0] : (obj[a[1]] && b.apply(this,[obj[a[1]]].concat([].slice.call(a,2))));
}
// method 1
get_if_exist(test,'level1.level2.level3');
// method 2
get_if_exist(test,'level1','level2','level3');
答案 52 :(得分:0)
另一个选项(接近this answer):
function resolve(root, path){
try {
return (new Function(
'root', 'return root.' + path + ';'
))(root);
} catch (e) {}
}
var tree = { level1: [{ key: 'value' }] };
resolve(tree, 'level1[0].key'); // "value"
resolve(tree, 'level1[1].key'); // undefined
答案 53 :(得分:0)
另一种解决方法是,例如,具有以下对象:
var x = {
a: {
b: 3
}
};
然后,我做的是将以下函数添加到此对象:
x.getKey = function(k){
var r ;
try {
r = eval('typeof this.'+k+' !== "undefined"');
}catch(e){
r = false;
}
if(r !== false){
return eval('this.'+k);
}else{
console.error('Missing key: \''+k+'\'');
return '';
}
};
然后你可以测试:
x.getKey('a.b');
如果未定义,则该函数返回&#34;&#34; (空字符串)否则返回现有值。
还请考虑检查链接的其他更复杂的解决方案:JS object has property deep check
Object.prototype.hasOwnNestedProperty = function(propertyPath){
if(!propertyPath)
return false;
var properties = propertyPath.split('.');
var obj = this;
for (var i = 0; i < properties.length; i++) {
var prop = properties[i];
if(!obj || !obj.hasOwnProperty(prop)){
return false;
} else {
obj = obj[prop];
}
}
return true;
};
// Usage:
var obj = {
innerObject:{
deepObject:{
value:'Here am I'
}
}
}
obj.hasOwnNestedProperty('innerObject.deepObject.value');
P.S。:还有一个递归版本。
答案 54 :(得分:0)
又一个版本:
function nestedPropertyExists(obj, props) {
var prop = props.shift();
return prop === undefined
? true
: obj.hasOwnProperty(prop) ? nestedPropertyExists(obj[prop], props) : false;
}
nestedPropertyExists({a:{b:{c:1}}}, ['a','b','c']); // returns true
nestedPropertyExists({a:{b:{c:1}}}, ['a','b','c','d']); // returns false
答案 55 :(得分:0)
CMS解决方案效果很好,但使用/语法可以更方便。 我建议关注
var checkNested = function(obj, structure) {
var args = structure.split(".");
for (var i = 0; i < args.length; i++) {
if (!obj || !obj.hasOwnProperty(args[i])) {
return false;
}
obj = obj[args[i]];
}
return true;
};
您可以使用点而不是提供多个参数来使用对象表示法
var test = {level1:{level2:{level3:'level3'}} };
checkNested(test, 'level1.level2.level3'); // true
checkNested(test, 'level1.level2.foo'); // false
答案 56 :(得分:0)
我写了library called l33teral to help test for nested properties。您可以像这样使用它:
var myObj = {/*...*/};
var hasNestedProperties = leet(myObj).probe('prop1.prop2.prop3');
我也喜欢这里的ES5 / 6解决方案。
答案 57 :(得分:0)
最好最简单的答案是:
var isDefinedPath = function (path) {
var items = path.split ('.');
if (!items || items.length < 1 || !(items[0] in window)) { return false; }
var buffer = [items[0]];
for (var i = 1, e = items.length; i < e; i ++) {
buffer.push (items[i]);
if (eval ('typeof(' + buffer.join ('.') + ') == "undefined"')) {
return false;
}
}
return true;
}
试验: isDefinedPath('level1.level2.level3');
第一级不能是数组,其他可以
答案 58 :(得分:0)
function isIn(string, object){
var arr = string.split(".");
var notFound = true;
var length = arr.length;
for (var i = 0; i < length; i++){
var key = arr[i];
if (!object.hasOwnProperty(key)){
notFound = false;
break;
}
if ((i + length) <= length){
object = object[key];
}
}
return notFound;
}
var musicCollection = {
hasslehoff: {
greatestHits : true
}
};
console.log(isIn("hasslehoff.greatestHits", musicCollection));
console.log(isIn("hasslehoff.worseHits", musicCollection));
这是我的基于字符串的分隔符版本。
答案 59 :(得分:0)
我正在以下列方式使用函数。
var a = {};
a.b = {};
a.b.c = {};
a.b.c.d = "abcdabcd";
function isDefined(objectChainString) {
try {
var properties = objectChainString.split('.');
var currentLevel = properties[0];
if (currentLevel in window) {
var consolidatedLevel = window[currentLevel];
for (var i in properties) {
if (i == 0) {
continue;
} else {
consolidatedLevel = consolidatedLevel[properties[i]];
}
}
if (typeof consolidatedLevel != 'undefined') {
return true;
} else {
return false;
}
} else {
return false;
}
} catch (e) {
return false;
}
}
// defined
console.log(checkUndefined("a.b.x.d"));
//undefined
console.log(checkUndefined("a.b.c.x"));
console.log(checkUndefined("a.b.x.d"));
console.log(checkUndefined("x.b.c.d"));
答案 60 :(得分:-3)
在typeScript中,您可以执行以下操作:
public static IEnumerable<object[]> InsertData() => Data;
public abstract/virtual IEnumerable<object[]> Data()
{
return ...;
}
答案 61 :(得分:-3)
今天刚写了这个函数,它深入搜索嵌套对象中的属性,如果找到则返回属性的值。
/**
* Performs a deep search looking for the existence of a property in a
* nested object. Supports namespaced search: Passing a string with
* a parent sub-object where the property key may exist speeds up
* search, for instance: Say you have a nested object and you know for
* certain the property/literal you're looking for is within a certain
* sub-object, you can speed the search up by passing "level2Obj.targetProp"
* @param {object} obj Object to search
* @param {object} key Key to search for
* @return {*} Returns the value (if any) located at the key
*/
var getPropByKey = function( obj, key ) {
var ret = false, ns = key.split("."),
args = arguments,
alen = args.length;
// Search starting with provided namespace
if ( ns.length > 1 ) {
obj = (libName).getPropByKey( obj, ns[0] );
key = ns[1];
}
// Look for a property in the object
if ( key in obj ) {
return obj[key];
} else {
for ( var o in obj ) {
if ( (libName).isPlainObject( obj[o] ) ) {
ret = (libName).getPropByKey( obj[o], key );
if ( ret === 0 || ret === undefined || ret ) {
return ret;
}
}
}
}
return false;
}