测试是否存在嵌套的JavaScript对象键

测试是否存在嵌套的JavaScript对象键,javascript,object,properties,nested,Javascript,Object,Properties,Nested,如果我对某个对象有引用: var test = {}; 可能(但不是立即)具有嵌套对象的对象,例如: {level1: {level2: {level3: "level3"}}}; 检查深度嵌套对象中是否存在属性的最佳方法是什么 警报(测试级别1)产生未定义,但发出警报(test.level1.level2.level3)失败 我目前正在做这样的事情: if(test.level1 && test.level1.level2 && test.level1.le

如果我对某个对象有引用:

var test = {};
可能(但不是立即)具有嵌套对象的对象,例如:

{level1: {level2: {level3: "level3"}}};
检查深度嵌套对象中是否存在属性的最佳方法是什么

警报(测试级别1)产生
未定义
,但发出
警报(test.level1.level2.level3)失败

我目前正在做这样的事情:

if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
    alert(test.level1.level2.level3);
}
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
if (o(o(o(o(test).level1).level2).level3)
{

}
  const test = test?.level1?.level2?.level3;
  if (test) alert(test);
但是我想知道是否有更好的办法。

怎么样

try {
   alert(test.level1.level2.level3)
} catch(e) {
 ...whatever

}

如果您不希望出现
类型错误
,则必须一步一步地执行此操作,因为如果其中一个成员为
null
未定义
,并且您尝试访问一个成员,将引发异常

您可以简单地
捕获
异常,或者创建一个函数来测试是否存在多个级别,如下所示:

if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
    alert(test.level1.level2.level3);
}
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
if (o(o(o(o(test).level1).level2).level3)
{

}
  const test = test?.level1?.level2?.level3;
  if (test) alert(test);
但是,如果您希望获取嵌套属性的值,而不仅仅是检查其存在性,那么这里有一个简单的单行函数:

var o = function(obj) { return obj || {};};
函数getNested(obj,…args){
返回参数reduce((对象,级别)=>obj&&obj[level],obj)
}
常量测试={level1:{level2:{level3:'level3'}};
log(getNested(测试'level1','level2','level3');/'三级'
log(getNested(测试'level1','level2','level3','length');//6.
log(getNested(测试'level1','level2','foo');//未定义

log(getNested(测试'a','b');//未定义
如果像字符串一样处理名称,则可以在任何深度读取对象属性:
'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');
如果任何段
未定义
,则返回
未定义

以下是模式I:


事实上,整篇文章讨论了如何使用javascript实现这一点。他决定将上述语法(一旦你习惯了就不难理解)作为一种习语使用。

一个简单的方法是:

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)的情况。

一个简短的ES5版本@CMS的优秀答案:

// 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
有一个函数将以安全的方式执行此操作。。。i、 e

safeRead(test, 'level1', 'level2', 'level3');

如果任何属性为null或未定义,则返回一个空字符串

CMS给出的答案可以正常工作,并对null检查进行以下修改

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;
    }
函数checkNested(obj/*,level1,level2,…levelN*/)
{
var args=Array.prototype.slice.call(参数),
obj=args.shift();
对于(变量i=0;i
以下选项从一开始就进行了详细阐述。两者的树相同:

var o = { a: { b: { c: 1 } } };

未定义时停止搜索
逐级保证
我尝试了一种递归方法:

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';
更新 看起来像lodash
满足所有嵌套属性的获取需求

_.get(countries, 'greece.sparta.playwright')


先前的答复 用户可以享受其中有一个

获取路径 签名:
.getPath(obj:Object,ks:String | Array)

获取嵌套对象中任意深度处的值,该值基于 钥匙是给你的。键可以作为数组或点分隔字符串给出。 如果无法到达路径,则返回未定义的

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
如果您在ES6环境中编码(或使用),则可以利用以下语法:

关于性能,如果设置了属性,则使用
try..catch
块不会导致性能损失。如果属性未设置,则会影响性能

考虑简单地使用:


我编写了自己的函数,该函数采用所需的路径,并且有一个好的回调函数和一个坏的回调函数

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();
}

我知道这个问题很老,但我想通过将它添加到所有对象来提供一个扩展。我知道人们倾向于不赞成使用对象原型来扩展对象功能,但我发现没有什么比这更容易的了。另外,现在允许使用该方法

为了测试它,特别是它包含一些jQuery,如果您直接修改Object.prototype,因为属性变得可枚举,那么jQuery就会中断。这在第三方库中应该可以很好地使用。

//以防万一您的框架不支持或不包括
//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
//*************************************************** Array.prototype.some=函数(fn,thisObj){ var范围=此OBJ | |窗口; 对于(变量i=0,j=this.length;i0){ return!childs.some(函数(项){ if(对象中的项){ 对象=对象[项目]; 返回false; }否则返回true; }); }如果(对象中的字符串){ 返回true; }否则返回false; } 变量对象={ 数据:{ 项目:{ 分项:{ 布莱:{ 在这里:{ 是的 } } } } } }; console.log(isSet(对象,'data.item'));//真的 log(isSet(对象,'x'));//假的 console.log(isSet(对象,'data.sub_item'));//假的 console.log(isSet(对象,'data.item'));//真的 log(isSet(对象,'data.item.sub_item.bla.here.iam');//真的
我认为这是一个轻微的改进(成为单行):

这是因为&&运算符返回
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") )
//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
   alert( test.level1 && test.level1.level2 && test.level1.level2.level3 )
    obj = {
        "l1":"something",
        "l2":[{k:0},{k:1}],
        "l3":{
            "subL":"hello"
        }
    }
if( obj._has( "something.['deep']['under'][1][0].item" ) ) {
    //do something
}
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;
}});
var o = function(obj) { return obj || {};};
if (o(o(o(o(test).level1).level2).level3)
{

}
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
o(o(o(o(o(test).level1).level2).level3, "none")
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 );
}
var r1 = (((test || {}).level1 || {}).level2 || {}).level3;
var r2 = (((test || {}).level1 || {}).level2 || {}).foo;
var r1 = test.level1 && test.level1.level2 && test.level1.level2.level3;
var r2 = test.level1 && test.level1.level2 && test.level1.level2.foo;
function checkNested(obj) {
  for (var i = 1; i < arguments.length; i++) {
    if (!obj.hasOwnProperty(arguments[i])) {
      return false;
    }
    obj = obj[arguments[i]];
  }
  return true;
}
function get_if_exist(str) {
    try { return eval(str) }
    catch(e) { return undefined }
}
function validChain( object, ...keys ) {
    return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}
function objHasKeys(obj, keys) {
  var next = keys.shift();
  return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}
function nestedPropertyExists(obj, props) {
    var prop = props.shift();
    return prop === undefined ? true : obj.hasOwnProperty(prop) ? nestedPropertyExists(obj[prop], props) : false;
}
function deeptest(target, s){
    s= s.split('.')
    var obj= target[s.shift()];
    while(obj && s.length) obj= obj[s.shift()];
    return obj;
}
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);
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;
}
const propExists = (obj, path) => {
    return !!path.split('.').reduce((obj, prop) => {
        return obj && obj[prop] ? obj[prop] : undefined;
    }, obj)
}
  const test = test?.level1?.level2?.level3;
  if (test) alert(test);
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'));
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