[javascript] 중첩 된 JavaScript 객체 키가 있는지 테스트

객체에 대한 참조가있는 경우 :

var test = {};

잠재적으로 (즉시는 아니지만) 중첩 된 객체를 가질 것입니다.

{level1: {level2: {level3: "level3"}}};

깊이 중첩 된 객체에 속성이 있는지 확인하는 가장 좋은 방법은 무엇입니까?

alert(test.level1);수율 undefined하지만,alert(test.level1.level2.level3); 실패합니다.

나는 현재 다음과 같은 일을하고있다 :

if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
    alert(test.level1.level2.level3);
}

그러나 더 좋은 방법이 있는지 궁금합니다.



답변

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

ES6 업데이트 :

다음은 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)
}

그러나 중첩 속성의 값을 가져 와서 존재 여부를 확인하려는 경우 간단한 한 줄 함수는 다음과 같습니다.

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

위의 함수를 사용하면 중첩 속성 값을 얻을 수 있습니다 undefined. 그렇지 않으면를 반환 합니다.

2019-10-17 업데이트 :

옵션 체인 제안 온 3 단계에 도달 ECMAScript를위원회 과정 이 허용됩니다 당신 토큰을 사용하여 안전하게 액세스 중첩 특성에 ?.새로운 옵션 체인 운영자 :

const value = obj?.level1?.level2?.level3 

액세스 된 레벨 중 하나가 null있거나 undefinedundefined자체 가 해결 되는 경우.

이 제안을 통해 메소드 호출을 안전하게 처리 할 수 ​​있습니다.

obj?.level1?.method();

상기 식을 생성 할 undefined경우 obj, obj.level1또는 obj.level1.method이다 null또는 undefined그렇지 않으면 그 함수를 호출한다.

옵션 체인 플러그인을 사용하여 Babel에서이 기능을 사용할 수 있습니다 .

이후 바벨 7.8.0 , ES2020은 기본적으로 지원됩니다

Babel REPL 에서이 예 를 확인하십시오 .

??UPDATE : 2019 년 12 월 ??

옵션 체인 제안은 마침내 TC39위원회의 2019 년 12 월 회의에서 4 단계에 도달했습니다 . 이는이 기능이 ECMAScript 2020 표준의 일부라는 것을 의미합니다 .


답변

Oliver Steele에서 얻은 패턴은 다음과 같습니다 .

var level3 = (((test || {}).level1 || {}).level2 || {}).level3;
alert( level3 );

사실 전체 기사는 자바 스크립트에서 어떻게 할 수 있는지에 대한 토론입니다. 그는 위의 구문을 사용하기로 결정했습니다 (이것에 익숙해지면 읽기가 어렵지 않습니다).


답변

최신 정보

lodash _.get 모든 중첩 속성 요구 사항을 추가 한 것 같습니다.

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

https://lodash.com/docs#get


이전 답변

lodash 사용자는 이 문제를 완화 하는 몇 가지 방법 이있는 lodash.contrib 를 즐길 수 있습니다 .

getPath

서명: _.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


답변

내가 수행 한 성능 테스트를 (당신에게 감사 cdMinix을 아래에 나열된 결과이 질문에 제안 된 아이디어에 대한 lodash를 추가).

고지 사항 # 1 문자열을 참조로 바꾸는 것은 불필요한 메타 프로그래밍이며 피하는 것이 가장 좋습니다. 시작하기 위해 참조를 잃어 버리지 마십시오. 이 질문에서 비슷한 질문에 대한 자세한 내용을 읽으십시오 .

고지 사항 # 2 여기서는 밀리 초당 수백만 건의 작업에 대해 이야기하고 있습니다. 이들 중 어느 것도 대부분의 사용 사례에서 큰 차이를 만들 가능성은 거의 없습니다. 각각의 한계를 아는 것이 가장 적합한 것을 선택하십시오. 나를 위해 나는 reduce편의 가 아닌 것과 함께 갈 것입니다 .

Object Wrap (Oliver Steele 제공) – 34 % – 가장 빠름

var r1 = (((test || {}).level1 || {}).level2 || {}).level3;
var r2 = (((test || {}).level1 || {}).level2 || {}).foo;

독창적 인 솔루션 (문제의 제안) – 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 %

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 %

function get_if_exist(str) {
    try { return eval(str) }
    catch(e) { return undefined }
}

유효한 체인 – 54 %

function validChain( object, ...keys ) {
    return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}

objHasKeys – 63 %

function objHasKeys(obj, keys) {
  var next = keys.shift();
  return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}

nestedPropertyExists – 69 %

function nestedPropertyExists(obj, props) {
    var prop = props.shift();
    return prop === undefined ? true : obj.hasOwnProperty(prop) ? nestedPropertyExists(obj[prop], props) : false;
}

_.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;
}

슬픈 광대 – 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);


답변

문자열처럼 이름을 처리하면 깊이에 상관없이 객체 속성을 읽을 수 있습니다 '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');

undefined세그먼트 중 하나 가 이면 반환 됩니다 undefined.


답변

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

당신이 ES6 환경에서 코딩 (또는 사용하는 경우 6to5를 ) 다음은 이용 취할 수 화살표 기능 구문 :

// ES6 using the arrow function
console.log(
    isset(() => a.b.c),
    isset(() => a.b.c.d.e.f)
);

성능과 관련하여 사용에 대한 성능 불이익은 없습니다 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


답변

어때?

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

}