// where this array is hundreds of entries long, with a mix
// of the two examples given
var test = [{'a':{'b':{'c':"foo"}}}, {'a': "bar"}];
这给我带来了问题,因为我有时需要遍历数组,而不一致给我这样的错误:
for (i=0; i<test.length; i++) {
// ok on i==0, but 'cannot read property of undefined' on i==1
console.log(a.b.c);
}
我知道我可以说
if(a.b){ console.log(a.b.c)}
,但这在最多嵌套5个或6个对象的情况下非常繁琐。还有其他(更简便的)方法可以让它仅在console.log存在的情况下执行,而不会引发错误吗?#1 楼
更新:如果您根据ECMAScript 2020或更高版本使用JavaScript,请参阅可选链接。
TypeScript在3.7版中添加了对可选链接的支持。
// use it like this
obj?.a?.lot?.of?.properties
ECMASCript 2020或早于3.7版的TypeScript之前的JavaScript解决方案:
一种快速的解决方法是使用try / catch具有ES6箭头功能的辅助功能:
function getSafe(fn, defaultVal) {
try {
return fn();
} catch (e) {
return defaultVal;
}
}
// use it like this
console.log(getSafe(() => obj.a.lot.of.properties));
// or add an optional default value
console.log(getSafe(() => obj.a.lot.of.properties, 'nothing'));
评论
我喜欢它!我唯一要添加的是catch内的console.warn,这样您就知道错误了,但是错误仍在继续。
–拉比·舒基·古尔
18/12/11在10:31
在不重新抛出的情况下捕获所有异常是不好的,并且通常将异常用作预期执行流的一部分也不太好-即使在这种情况下,异常包含得很好。
–hugo
19年8月25日在11:28
#2 楼
您正在做的事情引发了一个异常(理所当然的)。您总是可以做
try{
window.a.b.c
}catch(e){
console.log("YO",e)
}
但是我不会,而是考虑您的用例。
为什么要访问数据,嵌套了六个您不熟悉的级别?什么用例证明了这一点呢?
通常,您实际上想验证要处理的对象类型。
另外,在侧面说明中,请勿使用
if(a.b)
之类的语句,因为如果a.b为0或什至为“ 0”,它将返回false。而是检查a.b !== undefined
评论
关于您的第一次编辑:我正在处理JSON结构化的数据库条目,以便对象可以缩放多个级别的字段(即entry.users.messages.date等,其中并非所有情况下都输入了数据)
–阿里
13年2月8日在22:23
“如果a.b为0,它将返回true”-不。 typeof a.b ===“ undefined” && a.b!= null-不必在第一部分之后进行第二部分,而只做if(“ a”中的“ b”)就更有意义
–伊恩
13年2月8日在22:23
@Ian是的,我显然是相反的意思,即使a.b为“ 0”,它也会返回false。不错的收获
–本杰明·格伦鲍姆(Benjamin Gruenbaum)
13年2月8日在22:24
@BenjaminGruenbaum听起来不错,不确定您是否要那样做。另外,我认为您需要typeof a.b!==“ undefined” && a.b!= null`-注意!==
–伊恩
13年2月8日在22:26
如果您不想沉迷于a.b && a.b.c && console.log(a.b.c),那么这是一致地记录未知数的唯一方法。
–布赖恩·克雷
13年2月8日在22:31
#3 楼
如果使用lodash,则可以使用其“具有”功能。它与本机“ in”相似,但允许路径。var testObject = {a: {b: {c: 'walrus'}}};
if(_.has(testObject, 'a.b.c')) {
//Safely access your walrus here
}
评论
最好,我们可以使用带有默认值的_.get()以便于阅读:_.get(object,'a.b.c','default');
–如果不是
18年6月18日在13:53
#4 楼
如果我正确理解了您的问题,则需要最安全的方法来确定对象是否包含属性。最简单的方法是使用
in
运算符。window.a = "aString";
//window should have 'a' property
//lets test if it exists
if ("a" in window){
//true
}
if ("b" in window){
//false
}
当然,您可以根据需要将其嵌套得最深
if ("a" in window.b.c) { }
不确定是否有帮助。
评论
您无法安全地将其嵌套到所需的深度。如果未定义window.b怎么办?您将收到类型错误:无法使用'in'运算符在未定义的位置搜索'c'
–特雷弗
17年8月28日在23:34
#5 楼
尝试这个。如果未定义a.b
,则它将保留if
语句而没有任何异常。if (a.b && a.b.c) {
console.log(a.b.c);
}
#6 楼
当使用深度或复杂的json对象时,这是一个常见的问题,因此我尽量避免尝试/捕获或嵌入会使代码不可读的多个检查,我通常在所有过程中都使用这一小段代码来完成这项工作。 /* ex: getProperty(myObj,'aze.xyz',0) // return myObj.aze.xyz safely
* accepts array for property names:
* getProperty(myObj,['aze','xyz'],{value: null})
*/
function getProperty(obj, props, defaultValue) {
var res, isvoid = function(x){return typeof x === "undefined" || x === null;}
if(!isvoid(obj)){
if(isvoid(props)) props = [];
if(typeof props === "string") props = props.trim().split(".");
if(props.constructor === Array){
res = props.length>1 ? getProperty(obj[props.shift()],props,defaultValue) : obj[props[0]];
}
}
return typeof res === "undefined" ? defaultValue: res;
}
#7 楼
如果您有lodash,则可以使用其.get
方法 _.get(a, 'b.c.d.e')
或为其指定默认值
_.get(a, 'b.c.d.e', default)
#8 楼
我虔诚地使用不安全。它向下测试每个级别到您的对象,直到获得您要求的值或返回“未定义”。但绝不会出错。评论
类似于lodash _.get
–Filype
16年7月21日在3:32
好喊!如果不需要lodash的其他功能,它仍然很有用。
–马丁斯
16年7月22日在14:04
#9 楼
如果使用Babel,您已经可以在@ babel / plugin-proposal-optional-chaining Babel插件中使用可选的链接语法。这将使您可以替换为:console.log(a && a.b && a.b.c);
,并替换为:
console.log(a?.b?.c);
#10 楼
我喜欢曹寿光的回答,但我不喜欢每次执行调用时都将函数作为参数传递给getSafe函数。我修改了getSafe函数以接受简单参数和纯ES5。/**
* Safely get object properties.
* @param {*} prop The property of the object to retrieve
* @param {*} defaultVal The value returned if the property value does not exist
* @returns If property of object exists it is returned,
* else the default value is returned.
* @example
* var myObj = {a : {b : 'c'} };
* var value;
*
* value = getSafe(myObj.a.b,'No Value'); //returns c
* value = getSafe(myObj.a.x,'No Value'); //returns 'No Value'
*
* if (getSafe(myObj.a.x, false)){
* console.log('Found')
* } else {
* console.log('Not Found')
* }; //logs 'Not Found'
*
* if(value = getSafe(myObj.a.b, false)){
* console.log('New Value is', value); //logs 'New Value is c'
* }
*/
function getSafe(prop, defaultVal) {
return function(fn, defaultVal) {
try {
if (fn() === undefined) {
return defaultVal;
} else {
return fn();
}
} catch (e) {
return defaultVal;
}
}(function() {return prop}, defaultVal);
}
评论
它实际上与getSafe(myObj.x.c)不兼容。尝试使用最新版本的Chrome和Firefox。
–RickardElimää
19年1月30日在16:26
#11 楼
在str的答案中,如果属性未定义,将返回值“ undefined”而不是设置的默认值。有时这可能会导致错误。以下内容可确保在未定义属性或对象时始终返回defaultVal。const temp = {};
console.log(getSafe(()=>temp.prop, '0'));
function getSafe(fn, defaultVal) {
try {
if (fn() === undefined) {
return defaultVal
} else {
return fn();
}
} catch (e) {
return defaultVal;
}
}
评论
Hardy Le Roux对我的代码进行的改进版本不适用于let myObj = {} getSafe(()=> myObj.a.b,“ nice”),而我的却有效。有人解释为什么吗?
–曹寿光
7月23日在1:13
#12 楼
Lodash具有get
方法,该方法允许将默认值用作可选的第三个参数,如下所示: const myObject = {
has: 'some',
missing: {
vars: true
}
}
const path = 'missing.const.value';
const myValue = _.get(myObject, path, 'default');
console.log(myValue) // prints out default, which is specified above
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
#13 楼
想象一下,当且仅当x
为非空时,我们才想对x
应用一系列函数:if (x !== null) x = a(x);
if (x !== null) x = b(x);
if (x !== null) x = c(x);
现在,我们需要对
y
:if (y !== null) y = a(y);
if (y !== null) y = b(y);
if (y !== null) y = c(y);
与
z
相同:if (z !== null) z = a(z);
if (z !== null) z = b(z);
if (z !== null) z = c(z);
没有适当的抽象就可以看到,我们最终将一遍又一遍地复制代码。这样的抽象已经存在:Maybe monad。
Maybe monad同时具有值和计算上下文:
monad使值安全并适用函数。
计算上下文是在应用函数之前进行的空检查。
天真的实现将如下所示:
⚠️此实现仅用于说明仅目的!这不是应该做的事情,并且在许多层面上都是错误的。但是,这应该可以使您更好地了解我在说什么。
如您所见,没有什么可以破坏的:
我们将一系列功能应用于我们的值
如果在任何时候该值变为空(或未定义),我们就不再应用任何函数。
const abc = obj =>
Maybe
.of(obj)
.map(o => o.a)
.map(o => o.b)
.map(o => o.c)
.value;
const values = [
{},
{a: {}},
{a: {b: {}}},
{a: {b: {c: 42}}}
];
console.log(
values.map(abc)
);
<script>
function Maybe(x) {
this.value = x; //-> container for our value
}
Maybe.of = x => new Maybe(x);
Maybe.prototype.map = function (fn) {
if (this.value == null) { //-> computational context
return this;
}
return Maybe.of(fn(this.value));
};
</script>
附录1
我无法解释什么是monads,因为这不是本文的目的,并且那里的人们对此有更好的了解比我但是,正如Eric Elliot在历史博客文章中所说的,JavaScript Monads变得简单:
无论您的技能水平或对类别理论的理解如何,使用monad都会使您的代码更易于使用。未能利用monad可能会使您的代码难以使用(例如,回调地狱,嵌套的条件分支,更多的冗长性)。
附录2
以下是我如何使用monetjs的Maybe monad解决您的问题的方法
const prop = key => obj => Maybe.fromNull(obj[key]);
const abc = obj =>
Maybe
.fromNull(obj)
.flatMap(prop('a'))
.flatMap(prop('b'))
.flatMap(prop('c'))
.orSome('🌯')
const values = [
{},
{a: {}},
{a: {b: {}}},
{a: {b: {c: 42}}}
];
console.log(
values.map(abc)
);
<script src="https://www.unpkg.com/monet@0.9.0/dist/monet.js"></script>
<script>const {Maybe} = Monet;</script>
#14 楼
我之前回答过这个问题,而今天碰巧也在做类似的检查。一种简化的检查嵌套的虚线属性是否存在。您可以修改此值以返回值,或通过一些默认值来实现目标。function containsProperty(instance, propertyName) {
// make an array of properties to walk through because propertyName can be nested
// ex "test.test2.test.test"
let walkArr = propertyName.indexOf('.') > 0 ? propertyName.split('.') : [propertyName];
// walk the tree - if any property does not exist then return false
for (let treeDepth = 0, maxDepth = walkArr.length; treeDepth < maxDepth; treeDepth++) {
// property does not exist
if (!Object.prototype.hasOwnProperty.call(instance, walkArr[treeDepth])) {
return false;
}
// does it exist - reassign the leaf
instance = instance[walkArr[treeDepth]];
}
// default
return true;
}
在您的问题中,您可以执行以下操作:
let test = [{'a':{'b':{'c':"foo"}}}, {'a': "bar"}];
containsProperty(test[0], 'a.b.c');
#15 楼
我通常这样使用: var x = object.any ? object.any.a : 'def';
#16 楼
您可以通过在获取属性之前提供默认值来避免出现错误 var test = [{'a':{'b':{'c':"foo"}}}, {'a': "bar"}];
for (i=0; i<test.length; i++) {
const obj = test[i]
// No error, just undefined, which is ok
console.log(((obj.a || {}).b || {}).c);
}
这也适用于数组:
const entries = [{id: 1, name: 'Scarllet'}]
// Giving a default name when is empty
const name = (entries.find(v => v.id === 100) || []).name || 'no-name'
console.log(name)
评论
该错误可能是常规Javascript异常,因此请尝试try..catch语句。就是说,包含野生异构元素的数组对我来说似乎是一个设计问题。如果项目之间的结构不一致,那么检查是否存在问题呢?确实,我会使用if(a.b中的&&“ c”中的“ b”)。这可能是“乏味的”,但这就是您因为不一致而得到的...常规逻辑。
为什么要访问不存在的属性,为什么不知道对象的外观?
我能理解为什么有人不希望错误导致所有故障。您不能总是依靠对象的属性来存在或不存在。如果您有适当的东西可以处理对象格式错误的事件,那么您的代码将更加高效且不那么脆弱。
您会惊讶于现实生活中有多少个对象/数组格式错误