在我的代码中,我处理的数组中有一些条目,其中有许多对象彼此嵌套,而有些则没有。它看起来像下面这样:
// 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,如果它存在,但没有抛出错误?
答案 0 :(得分:60)
快速解决方法是使用带有ES6 arrow function的try / catch帮助函数:
function getSafe(fn, defaultVal) {
try {
return fn();
} catch (e) {
return defaultVal;
}
}
// use it like this
getSafe(() => obj.a.lot.of.properties);
// or add an optional default value
getSafe(() => obj.a.lot.of.properties, 'nothing');
有关详细信息,请参阅this article。
答案 1 :(得分:42)
你在做什么会引发异常(这是理所当然的)。
你可以随时
try{
window.a.b.c
}catch(e){
console.log("YO",e)
}
但我不会想到你的用例。
为什么要访问数据,6级嵌套你不熟悉?什么用例证明了这一点?
通常,您希望实际验证您正在处理的对象类型。
此外,在旁注中,您不应使用if(a.b)
之类的语句,因为如果a.b为0,或者即使它是&#34; 0&#34;它将返回false。而是检查是否a.b !== undefined
答案 2 :(得分:14)
如果我正确理解您的问题,您需要最安全的方法来确定对象是否包含属性。
最简单的方法是使用“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) { }
不确定这是否有帮助。
答案 3 :(得分:11)
如果您使用lodash,则可以使用“has”功能。它类似于原生的“in”,但允许路径。
var testObject = {a: {b: {c: 'walrus'}}};
if(_.has(testObject, 'a.b.c')) {
//Safely access your walrus here
}
答案 4 :(得分:7)
试试这个。如果a.b
是不可取的,它会毫无例外地留下if
语句。
if (a.b && a.b.c) {
console.log(a.b.c);
}
答案 5 :(得分:5)
这是处理深度或复杂json对象时的常见问题,所以我尽量避免使用try / catch或嵌入多次检查会导致代码无法读取,我通常会在我的所有procect中使用这一小段代码来做这份工作。
/* 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;
}
答案 6 :(得分:4)
我虔诚地使用undefsafe。它会测试每个级别到您的对象,直到它获得您要求的值,或者返回“undefined”。但绝不是错误。
答案 7 :(得分:3)
我喜欢曹寿光的回答,但是我不喜欢每次执行调用时都将函数作为参数传递给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);
}
答案 8 :(得分:2)
在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;
}
}
答案 9 :(得分:1)
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>
答案 10 :(得分:1)
想象一下,当且仅当x
为非null时,我们才想对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。
也许 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
我无法解释什么是monad,因为这不是本文的目的,而且那里的人比我更好。但是,正如埃里克·埃利奥特(Eric Elliot)在历史博文JavaScript Monads Made Simple中所说:
无论您的技能水平或对类别理论的理解如何,使用monad都会使您的代码更易于使用。无法利用monad可能会使您的代码难以使用(例如,回调地狱,嵌套的条件分支,更多的冗长性)。
附录2
以下是我使用monetjs
中的也许单子来解决您的问题的方法
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>
答案 11 :(得分:1)
如果使用Babel,您已经可以在@babel/plugin-proposal-optional-chaining Babel plugin中使用可选的链接语法。这将允许您替换它:
console.log(a && a.b && a.b.c);
与此:
console.log(a?.b?.c);
答案 12 :(得分:1)
答案 13 :(得分:0)
与问题的实际问题无关,但可能对来此问题寻找答案的人有用。
检查您的函数参数。
如果你有一个像 const x({ a }) => { }
这样的函数,并且你不带参数调用它 x()
;将 = {}
附加到参数:const x({ a } = {}) => { }
。
我有一个这样的功能:
const x = ({ a }) => console.log(a);
// This one works as expected
x({ a: 1 });
// This one errors out
x();
结果为 "Uncaught TypeError: Cannot destructure property 'a' of 'undefined' as it is undefined."
const x = ({ a } = {}) => console.log(a);
// This one works as expected
x({ a: 1 });
// This now works too!
x();
答案 14 :(得分:0)
在获取属性之前,可以通过提供默认值来避免出错
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)
答案 15 :(得分:0)
我有时这样写:
proc.execFile("myapp/venv/bin/pip", { args: ["install", "myPipModule"]}, function() {
// do stuff
});
尽管可读性较差,但它可能比console.log((a.b || {}).c);
更有效。
答案 16 :(得分:0)
我通常这样使用:
var x = object.any ? object.any.a : 'def';
答案 17 :(得分:0)
我之前回答过这个问题,今天碰巧也在做类似的检查。检查嵌套的点缀属性是否存在的简化。您可以修改此值以返回值,或者通过一些默认值来实现目标。
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');