use*_*716 616 javascript nested properties object
如果我有一个对象的引用:
var test = {};
Run Code Online (Sandbox Code Playgroud)
可能(但不是立即)有嵌套对象,如:
{level1: {level2: {level3: "level3"}}};
Run Code Online (Sandbox Code Playgroud)
在最深层嵌套的对象中测试密钥是否存在的最佳方法是什么?
alert(test.level1);
收益率undefined
,但alert(test.level1.level2.level3);
失败了.
我现在正在做这样的事情:
if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
alert(test.level1.level2.level3);
}
Run Code Online (Sandbox Code Playgroud)
但我想知道是否有更好的方法.
CMS*_*CMS 383
如果您不想要a 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
Run Code Online (Sandbox Code Playgroud)
Gab*_*art 347
这是我从Oliver Steele那里得到的模式:
var level3 = (((test || {}).level1 || {}).level2 || {}).level3;
alert( level3 );
Run Code Online (Sandbox Code Playgroud)
事实上,整篇文章讨论了如何在javascript中执行此操作.他习惯于使用上面的语法(一旦你习惯它就不难读)作为一个成语.
Aus*_*ray 250
看起来像lodash 已添加 _.get
所有嵌套属性获取需求.
_.get(countries, 'greece.sparta.playwright')
Run Code Online (Sandbox Code Playgroud)
lodash用户可能喜欢lodash.contrib,它有几种方法可以缓解这个问题.
签名: _.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
Run Code Online (Sandbox Code Playgroud)
uni*_*rio 194
我已经对下面列出的结果对这个问题提出的一些建议进行了性能测试(感谢cdMinix添加lodash).
免责声明#1将字符串转换为引用是不必要的元编程,最好避免使用.不要忘记开头的参考资料.从这个问题的答案中了解更多.
免责声明#2我们在谈论每毫秒数百万次操作.在大多数用例中,这些都不太可能产生太大的影响.了解每个人的局限性,选择哪个最有意义.对我来说,我会选择一些
reduce
方便的东西.
Object Wrap(由Oliver Steele提供) - 34% - 最快
var r1 = (((test || {}).level1 || {}).level2 || {}).level3;
var r2 = (((test || {}).level1 || {}).level2 || {}).foo;
Run Code Online (Sandbox Code Playgroud)
原始解决方案(建议问题) - 45%
var r1 = test.level1 && test.level1.level2 && test.level1.level2.level3;
var r2 = test.level1 && test.level1.level2 && test.level1.level2.foo;
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
get_if_exist - 52%
function get_if_exist(str) {
try { return eval(str) }
catch(e) { return undefined }
}
Run Code Online (Sandbox Code Playgroud)
有效链接 - 54%
function validChain( object, ...keys ) {
return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}
Run Code Online (Sandbox Code Playgroud)
objHasKeys - 63%
function objHasKeys(obj, keys) {
var next = keys.shift();
return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}
Run Code Online (Sandbox Code Playgroud)
nestedPropertyExists - 69%
function nestedPropertyExists(obj, props) {
var prop = props.shift();
return prop === undefined ? true : obj.hasOwnProperty(prop) ? nestedPropertyExists(obj[prop], props) : false;
}
Run Code Online (Sandbox Code Playgroud)
_.get - 72%
最深的 - 86%
function deeptest(target, s){
s= s.split('.')
var obj= target[s.shift()];
while(obj && s.length) obj= obj[s.shift()];
return obj;
}
Run Code Online (Sandbox Code Playgroud)
悲伤的小丑 - 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);
Run Code Online (Sandbox Code Playgroud)
ken*_*bec 44
如果你像字符串一样处理名称,你可以读取任何深度的对象属性:'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');
Run Code Online (Sandbox Code Playgroud)
undefined
如果有任何段,则返回undefined
.
Gaj*_*jus 27
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; })
);
Run Code Online (Sandbox Code Playgroud)
如果您在ES6环境中编码(或使用6to5),那么您可以利用箭头函数语法:
// ES6 using the arrow function
console.log(
isset(() => a.b.c),
isset(() => a.b.c.d.e.f)
);
Run Code Online (Sandbox Code Playgroud)
关于性能,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
Run Code Online (Sandbox Code Playgroud)
use*_*291 23
怎么样
try {
alert(test.level1.level2.level3)
} catch(e) {
...whatever
}
Run Code Online (Sandbox Code Playgroud)
Dan*_*iel 19
这个问题很老了。今天你可以使用可选链(?.)
let value = test?.level1?.level2?.level3;
Run Code Online (Sandbox Code Playgroud)
来源:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
Fra*_*cke 18
const propExists = (obj, path) => {
return !!path.split('.').reduce((obj, prop) => {
return obj && obj[prop] ? obj[prop] : undefined;
}, obj)
}
Run Code Online (Sandbox Code Playgroud)
Gor*_*.it 14
你也可以使用tc39可选链接提议和babel 7 - tc39-proposal-optional-chaining
代码看起来像这样:
const test = test?.level1?.level2?.level3;
if (test) alert(test);
Run Code Online (Sandbox Code Playgroud)
jro*_*ode 10
我尝试了一种递归方法:
function objHasKeys(obj, keys) {
var next = keys.shift();
return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}
Run Code Online (Sandbox Code Playgroud)
该! 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
Run Code Online (Sandbox Code Playgroud)
我用它来打印一堆具有未知键/值的对象的友好html视图,例如:
var biosName = objHasKeys(myObj, 'MachineInfo:BiosInfo:Name'.split(':'))
? myObj.MachineInfo.BiosInfo.Name
: 'unknown';
Run Code Online (Sandbox Code Playgroud)
Veg*_*ter 10
我认为以下脚本提供了更具可读性的表示.
声明一个函数:
var o = function(obj) { return obj || {};};
Run Code Online (Sandbox Code Playgroud)
然后像这样使用它:
if (o(o(o(o(test).level1).level2).level3)
{
}
Run Code Online (Sandbox Code Playgroud)
我把它称为"悲伤的小丑技术",因为它使用的是标志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;
}
Run Code Online (Sandbox Code Playgroud)
用法是一样的:
o(o(o(o(test).level1).level2).level3
Run Code Online (Sandbox Code Playgroud)
但这次intellisense工作!
另外,您可以设置默认值:
o(o(o(o(o(test).level1).level2).level3, "none")
Run Code Online (Sandbox Code Playgroud)
创建全局function
并在整个项目中使用
尝试这个
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))
Run Code Online (Sandbox Code Playgroud)
如果条件
if(isExist(()=>obj.test.foo)){
....
}
Run Code Online (Sandbox Code Playgroud)
一种简单的方法是这样的:
try {
alert(test.level1.level2.level3);
} catch(e) {
alert("undefined"); // this is optional to put any output here
}
Run Code Online (Sandbox Code Playgroud)
try/catch
当未定义任何更高级别的对象(例如test,test.level1,test.level1.level2)时,将捕获情况。
我没有看到有人使用Proxies的例子
所以我提出了自己的想法.关于它的好处是你不必插入字符串.你实际上可以返回一个可链式的对象函数,并用它做一些神奇的事情.您甚至可以调用函数并获取数组索引来检查深层对象
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
Run Code Online (Sandbox Code Playgroud)
上面的代码适用于同步的东西.但是你会如何测试像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'])
})
Run Code Online (Sandbox Code Playgroud)
确定你可以使用async/await来摆脱一些回调.但是,如果你能做得更神奇呢?看起来像这样的东西:
fetch('https://httpbin.org/get').json().headers['User-Agent']
Run Code Online (Sandbox Code Playgroud)
你可能想知道所有的承诺和.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
Run Code Online (Sandbox Code Playgroud)
你可以尝试一下Optional chaining
(但要注意浏览器兼容性)。
let test = {level1: {level2: {level3: 'level3'}}};
let level3 = test?.level1?.level2?.level3;
console.log(level3); // level3
level3 = test?.level0?.level1?.level2?.level3;
console.log(level3); // undefined
Run Code Online (Sandbox Code Playgroud)
有一个 babel 插件(@babel/plugin-proposal-optional-chaining
)用于可选更改。因此,如有必要,请升级您的 babel。
基于此答案,我想出了此通用函数,可用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 );
}
Run Code Online (Sandbox Code Playgroud)
我创建了一个小函数来安全地获取嵌套对象的属性。
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);
}
Run Code Online (Sandbox Code Playgroud)
或更简单但稍微不可读的版本:
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);
}
Run Code Online (Sandbox Code Playgroud)
甚至更短,但不会在虚假标志上回退:
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);
}
Run Code Online (Sandbox Code Playgroud)
我测试了:
const obj = {
c: {
a: 2,
b: {
c: [1, 2, 3, {a: 15, b: 10}, 15]
},
c: undefined,
d: null
},
d: ''
}
Run Code Online (Sandbox Code Playgroud)
这是一些测试:
// 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'));
Run Code Online (Sandbox Code Playgroud)
要查看所有带有文档的代码和我尝试过的测试,可以检查我的github内容:https : //gist.github.com/vsambor/3df9ad75ff3de489bbcb7b8c60beebf4#file-javascriptgetnestedvalues-js
小智 5
我已使用此函数来访问深度嵌套对象的属性,它对我有用......
这是函数
/**
* 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;
}
Run Code Online (Sandbox Code Playgroud)
这就是我访问深层嵌套对象属性的方式
{{ getProperty(object, 'passengerDetails.data.driverInfo.currentVehicle.vehicleType') }}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
211063 次 |
最近记录: |