测试是否存在嵌套的 JavaScript 对象键
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2631001/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Test for existence of nested JavaScript object key
提问by user113716
If I have a reference to an object:
如果我有一个对象的引用:
var test = {};
that will potentially (but not immediately) have nested objects, something like:
这可能(但不是立即)具有嵌套对象,例如:
{level1: {level2: {level3: "level3"}}};
What is the best way to check for the existence of property in deeply nested objects?
检查深度嵌套对象中是否存在属性的最佳方法是什么?
alert(test.level1);yields undefined, but alert(test.level1.level2.level3);fails.
alert(test.level1);yield undefined,但alert(test.level1.level2.level3);失败了。
I'm currently doing something like this:
我目前正在做这样的事情:
if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
alert(test.level1.level2.level3);
}
but I was wondering if there's a better way.
但我想知道是否有更好的方法。
采纳答案by CMS
You have to do it step by step if you don't want a TypeErrorbecause if one of the members is nullor undefined, and you try to access a member, an exception will be thrown.
如果您不想要 a,则必须逐步进行,TypeError因为如果其中一个成员是nullor undefined,而您尝试访问某个成员,则会引发异常。
You can either simply catchthe exception, or make a function to test the existence of multiple levels, something like this:
您可以简单地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
ES6 UPDATE:
ES6 更新:
Here is a shorter version of the original function, using ES6 features and recursion (it's also in proper tail callform):
这是原始函数的较短版本,使用 ES6 特性和递归(它也是正确的尾调用形式):
function checkNested(obj, level, ...rest) {
if (obj === undefined) return false
if (rest.length == 0 && obj.hasOwnProperty(level)) return true
return checkNested(obj[level], ...rest)
}
However, if you want to get the value of a nested property and not only check its existence, here is a simple one-line function:
但是,如果您想获取嵌套属性的值而不只是检查其是否存在,这里有一个简单的单行函数:
function getNested(obj, ...args) {
return args.reduce((obj, level) => obj && obj[level], obj)
}
const test = { level1:{ level2:{ level3:'level3'} } };
console.log(getNested(test, 'level1', 'level2', 'level3')); // 'level3'
console.log(getNested(test, 'level1', 'level2', 'level3', 'length')); // 6
console.log(getNested(test, 'level1', 'level2', 'foo')); // undefined
console.log(getNested(test, 'a', 'b')); // undefined
The above function allows you to get the value of nested properties, otherwise will return undefined.
上面的函数可以让你获取嵌套属性的值,否则会返回undefined.
UPDATE 2019-10-17:
更新 2019-10-17:
The optional chaining proposalreached Stage 3 on the ECMAScript committee process, this will allow you to safely access deeply nested properties, by using the token ?., the new optional chaining operator:
在可选的链接建议对达到第3阶段ECMAScript委员会的过程,这将让你安全地访问深度嵌套的性质,通过使用令牌?.,新的可选链接操作:
const value = obj?.level1?.level2?.level3
If any of the levels accessed is nullor undefinedthe expression will resolve to undefinedby itself.
如果访问的任何级别是null或undefined表达式将undefined自行解析。
The proposal also allows you to handle method calls safely:
该提案还允许您安全地处理方法调用:
obj?.level1?.method();
The above expression will produce undefinedif obj, obj.level1, or obj.level1.methodare nullor undefined, otherwise it will call the function.
上面的表达式会产生undefinedif obj, obj.level1, or obj.level1.methodare nullor undefined,否则会调用函数。
You can start playing with this feature with Babel using the optional chaining plugin.
您可以使用可选的链接插件在 Babel 中开始使用此功能。
Since Babel 7.8.0, ES2020 is supported by default
从Babel 7.8.0 开始,默认支持 ES2020
Check this exampleon the Babel REPL.
在 Babel REPL 上查看此示例。
UPDATE: December 2019
更新:2019 年 12 月
The optional chaining proposal finally reached Stage 4in the December 2019 meeting of the TC39 committee. This means this feature will be part of the ECMAScript 2020Standard.
可选链提案最终在 2019 年 12 月的 TC39 委员会会议上进入第 4 阶段。这意味着此功能将成为ECMAScript 2020标准的一部分。
回答by Gabe Moothart
Here is a pattern I picked up from Oliver Steele:
这是我从奥利弗斯蒂尔那里学到的一个模式:
var level3 = (((test || {}).level1 || {}).level2 || {}).level3;
alert( level3 );
In fact that whole article is a discussion of how you can do this in javascript. He settles on using the above syntax (which isn't that hard to read once you get used to it) as an idiom.
事实上,整篇文章都是关于如何在 javascript 中做到这一点的讨论。他决定使用上述语法(一旦你习惯了它就不难阅读)作为习惯用法。
回答by Austin Pray
Update
更新
Looks like lodash has added_.getfor all your nested property getting needs.
看起来 lodash已经_.get为您所有的嵌套属性获取需求添加了。
_.get(countries, 'greece.sparta.playwright')
Previous answer
上一个答案
lodashusers may enjoy lodash.contribwhich has a couple methods that mitigate this problem.
lodash用户可能会喜欢lodash.contrib,它有几种方法可以缓解这个问题。
getPath
获取路径
Signature:_.getPath(obj:Object, ks:String|Array)
签名:_.getPath(obj:Object, ks:String|Array)
Gets the value at any depth in a nested object based on the path described by
the keys given. Keys may be given as an array or as a dot-separated string.
Returns undefinedif the path cannot be reached.
根据给定的键描述的路径获取嵌套对象中任何深度的值。键可以作为数组或以点分隔的字符串形式给出。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
回答by unitario
I have done performance tests(thank you cdMinixfor adding lodash) on some of the suggestions proposed to this question with the results listed below.
我已经对针对这个问题提出的一些建议进行了性能测试(感谢cdMinix添加 lodash),结果如下所示。
Disclaimer #1Turning strings into references is unnecessary meta-programming and probably best avoided. Don't lose track of your references to begin with. Read more from this answer to a similar question.
Disclaimer #2We are talking about millions of operations per millisecond here. It is very unlikely any of these would make much difference in most use cases. Choose whichever makes the most sense knowing the limitations of each. For me I would go with something like
reduceout of convenience.
免责声明 #1将字符串转换为引用是不必要的元编程,最好避免。一开始不要忘记您的参考文献。从这个对类似问题的回答中阅读更多内容。
免责声明 #2我们在这里讨论的是每毫秒数百万次操作。在大多数用例中,这些中的任何一个都不太可能产生太大的不同。了解每种方法的局限性,选择最有意义的方法。对我来说
reduce,为了方便,我会选择类似的东西。
Object Wrap (by Oliver Steele)– 34 % – fastest
Object Wrap(由 Oliver Steele 提供)– 34 % – 最快
var r1 = (((test || {}).level1 || {}).level2 || {}).level3;
var r2 = (((test || {}).level1 || {}).level2 || {}).foo;
Original solution (suggested in question)– 45%
原始解决方案(有问题的建议)– 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%
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%
get_if_exist– 52%
function get_if_exist(str) {
try { return eval(str) }
catch(e) { return undefined }
}
validChain– 54%
有效链– 54%
function validChain( object, ...keys ) {
return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}
objHasKeys– 63%
objHasKeys– 63%
function objHasKeys(obj, keys) {
var next = keys.shift();
return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}
nestedPropertyExists– 69%
NestedPropertyExists– 69%
function nestedPropertyExists(obj, props) {
var prop = props.shift();
return prop === undefined ? true : obj.hasOwnProperty(prop) ? nestedPropertyExists(obj[prop], props) : false;
}
_.get– 72%
_.get– 72%
deeptest– 86%
深度测试– 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% – slowest
悲伤的小丑– 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);
回答by kennebec
You can read an object property at any depth, if you handle the name like a string: 't.level1.level2.level3'.
您可以在任何深度阅读的对象属性,如果你处理的名称,如字符串:'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');
It returns undefinedif any of the segments is undefined.
undefined如果任何段是,则返回undefined。
回答by Gajus
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; })
);
If you are coding in ES6 environment (or using 6to5) then you can take advantage of the arrow functionsyntax:
如果您在 ES6 环境中编码(或使用6to5),那么您可以利用箭头函数语法:
// ES6 using the arrow function
console.log(
isset(() => a.b.c),
isset(() => a.b.c.d.e.f)
);
Regarding the performance, there is no performance penalty for using try..catchblock if the property is set. There is a performance impact if the property is unset.
关于性能,try..catch如果设置了该属性,则使用块没有性能损失。如果未设置该属性,则会影响性能。
Consider simply using _.has:
考虑简单地使用_.has:
var object = { 'a': { 'b': { 'c': 3 } } };
_.has(object, 'a');
// → true
_.has(object, 'a.b.c');
// → true
_.has(object, ['a', 'b', 'c']);
// → true
回答by user187291
how about
怎么样
try {
alert(test.level1.level2.level3)
} catch(e) {
...whatever
}
回答by Frank Nocke
ES6 answer, thoroughly tested :)
ES6 答案,经过彻底测试:)
const propExists = (obj, path) => {
return !!path.split('.').reduce((obj, prop) => {
return obj && obj[prop] ? obj[prop] : undefined;
}, obj)
}
→see Codepen with full test coverage
→请参阅具有完整测试覆盖率的 Codepen
回答by Goran.it
You can also use tc39 optional chaining proposal together with babel 7 - tc39-proposal-optional-chaining
您还可以将 tc39 optional chaining proposal 与 babel 7 一起使用 - tc39-proposal-optional-chaining
Code would look like this:
代码如下所示:
const test = test?.level1?.level2?.level3;
if (test) alert(test);
回答by jrode
I tried a recursive approach:
我尝试了一种递归方法:
function objHasKeys(obj, keys) {
var next = keys.shift();
return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}
The ! keys.length ||kicks out of the recursion so it doesn't run the function with no keys left to test. Tests:
该! 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
I am using it to print a friendly html view of a bunch of objects with unknown key/values, e.g.:
我正在使用它来打印一堆具有未知键/值的对象的友好 html 视图,例如:
var biosName = objHasKeys(myObj, 'MachineInfo:BiosInfo:Name'.split(':'))
? myObj.MachineInfo.BiosInfo.Name
: 'unknown';

