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


692

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

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

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


1
최근 요청 된 접선 관련 질문을 확인 할 수 있습니다 stackoverflow.com/questions/2525943/...
아 누락


거기에 몇 가지 제안 : stackoverflow.com/a/18381564/1636522
leaf

level3 속성이 false 인 경우 현재 접근 방식에 잠재적 인 문제가 있습니다.이 경우 속성이 존재하더라도이 예제를 살펴보십시오. jsfiddle.net/maz9bLjx
GibboK

10
try catch를 사용해도됩니다
Raghavendra

답변:


489

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 표준의 일부라는 것을 의미합니다 .


4
arguments실제로 배열이 아닙니다. Array.prototype.slice.call(arguments)그것을 공식 배열로 변환합니다. Learn
deefour

23
수 this'd 더 효율적 많이var obj = arguments[0];과에서 시작하는 var i = 1대신에 복사하는 arguments객체
Claudiu

2
긴축을 위해 try / catch가있는 버전을 구성했으며 놀라운 것은 아닙니다. 어떤 이유로 Safari에서는 예외입니다. Claudiu의 수정과 함께 선택된 답변보다 훨씬 더 성능이 좋은 아래의 답변이 있습니다. 여기 jsperf
netpoetica

3
ES6에서 args변수 선언을 제거하고 메소드 ...args 의 두 번째 인수로 사용할 수 있습니다 checkNested. developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Vernon

7
이것은 매우 유지 보수가 불가능합니다. 속성 키가 변경되면 프로젝트의 모든 개발자는 전체 코드베이스를 '문자열 검색'해야합니다. 훨씬 더 큰 문제를 야기하기 때문에 이것은 실제로 문제에 대한 해결책이 아닙니다
Drenai

357

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

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

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


8
@wared 나는 그것이 얼마나 간결한 지에 대해 흥미 롭다고 생각한다. 링크 된 게시물의 성능 특성에 대한 자세한 설명이 있습니다. 예, 항상 모든 테스트를 수행하지만 임시 변수 생성을 피하고 매번 빈 객체를 새로 만드는 오버 헤드를 방지하려면 {} 별칭을 var에 별칭으로 지정할 수 있습니다. 99 %의 경우 속도가 중요하지 않을 것으로 예상되며 프로파일 링의 대안이없는 경우.
Gabe Moothart

9
@MuhammadUmer 아니요, 요점은 (test || {})테스트가 정의되지 않은 경우 수행하는 것 ({}.level1 || {})입니다. 물론, {}.level1정의되지 않았으므로, 등을 수행하고 있음을 의미합니다 {}.level2.
Joshua Taylor

3
@JoshuaTaylor : test선언되지 않은 경우 ReferenceError 가 있지만 이것이 선언되지 않은 경우 수정해야 할 버그가 있기 때문에 문제가되지 않습니다. 그래서 오류가 좋은 것입니다.

34
당신은 말했다 "하지 않은 어려운 읽고 나면 당신이 익숙해 그것" . 글쎄, 이것들 은 이미 이것이 엉망 이라는 것을 알고 있는 징후 입니다. 그렇다면 왜이 솔루션을 제안합니까? 오타가 발생하기 쉽고 가독성에 전혀 영향미치지 않습니다 . 그냥보세요! 내가하면 해야 못생긴 라인을 쓰기는 할 수 asswell해야 읽을 ; 그래서 난 그냥 막대기로 갈거야if(test.level1 && test.level1.level2 && test.level1.level2.level3)
Sharky

8
내가 빠진 것이 아닌 한, 이것은 거짓 일 수도있는 부울 엔드 속성에는 작동하지 않습니다 ... 슬프게도. 그렇지 않으면 나는이 관용구를 좋아한다.
T3db0t 2016

261

최신 정보

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

Lodash에는 실제로 _.isPathDefined (obj, pathString) 메소드가 필요합니다.
Matthew Payne

@MatthewPayne 아마도 좋을지 모르지만 실제로는 필요하지 않습니다. 정말 쉽게 할 수 있습니다function isPathDefined(object, path) { return typeof _.getPath(object, path) !== 'undefined'; }
Thor84no

11
Lodash는 이와 동일한 기능을 가지고 있습니다._.get(countries, 'greece.sparta.playwright', 'default'); // → 'default' _.has(countries, 'greece.spart.playwright') // → false
Tom

더 나은 _.result 것입니다
Shishir Arora

여러 다른 경로를 결정해야하는 경우 다음을 고려하십시오. var url = _.get(e, 'currentTarget.myurl', null) || _.get(e, 'currentTarget.attributes.myurl.nodeValue', null) || null
Simon Hutchison

210

내가 수행 한 성능 테스트를 (당신에게 감사 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);

16
느린 것이있다 - 테스트가 갖는 더 %가 주목해야한다
avalanche1

2
lodash는 어떻 _.get()습니까? 그 답변과 비교하여 얼마나 성능이 좋습니까?
beniutek

1
이들의 각 방법은 상황에 따라 다른 방법보다 느리거나 빠릅니다. 모든 키가 발견되면 "객체 랩"이 가장 빠를 수 있지만 키 중 하나를 찾지 못하면 "기본 솔루션 / 원본 솔루션"이 더 빠를 수 있습니다.
evilReiko

1
@evilReiko 어떤 키도 발견되지 않으면 어떤 방법도 느려지지만 서로 비례하여 여전히 거의 동일합니다. 그러나 당신 말이 맞습니다. 이것은 다른 어떤 것보다 지적 운동입니다. 여기서는 밀리 초당 백만 회 반복에 대해 이야기하고 있습니다. 큰 차이가 나는 유스 케이스는 없습니다. 개인적으로 나는 편의를 위해 reduce또는 try/catch밖으로 나갈 것입니다.
unitario

어떻게 성능이 좋은이 비교되는try { test.level1.level2.level3 } catch (e) { // some logger e }
렉스

46

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


3
이 방법은 적어도 Chrome에서는 성능이 뛰어나며 경우에 따라 선택된 답변의 @Claudiu 수정 버전보다 성능이 우수합니다. 성능 테스트 참조 : jsperf.com/check-if-deep-property-exists-with-willnotthrow
netpoetica

28
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

2
나는 그 try-catch접근법이 최선의 해결책 이라고 생각합니다 . 객체의 유형에 대해 쿼리하는 것과 API가 존재한다고 가정하고 존재하지 않으면 실패한다는 철학적 차이가 있습니다. 후자는 느슨한 유형의 언어에 더 적합합니다. stackoverflow.com/a/408305/2419669를 참조하십시오 . 이 try-catch방법은보다 명확합니다 if (foo && foo.bar && foo.bar.baz && foo.bar.baz.qux) { ... }.
yangmillstheory

24

어때?

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

}

15
try / catch가 객체의 존재를 테스트하는 좋은 방법이라고 생각하지 않습니다. try / catch는 테스트와 같은 일반적인 조건이 아닌 예외를 처리하기위한 것입니다. 각 단계에서 (typeof foo == "undefined")가 더 낫다고 생각합니다. 일반적으로 깊이 중첩 된 속성으로 작업하는 경우 약간의 리팩토링이 필요할 것입니다. 또한 try / catch는 예외가 발생하면 Firebug (및 오류 발생이 설정된 모든 브라우저)에서 중단을 발생시킵니다.
Sam Dutton

다른 솔루션을 사용하면 브라우저가 존재를 두 번 확인하기 때문에 이것에 투표합니다. ‘acb = 2’라고 부르고 싶다고합시다. 브라우저 값을 수정하기 전에 존재 확인해야합니다 (그렇지 않으면 OS에서 메모리 오류가 발생 함).

4
문제는 여전히 남아 있습니다. 마법사가 1 hasOwnProperty()회 시도를 시도하거나 n 번 호출하는 것이 더 빠릅 니까?

14
왜이게 또 나쁜가요? 이것은 나에게 가장 깨끗해 보인다.
오스틴기도

나는 말할 것입니다 : 당신이 그 속성이 존재한다고 생각한다면 그것을 try 블록으로 감싸는 것이 좋습니다. 그런 다음 존재하지 않으면 오류입니다. 그러나 게으르고 속성이 존재하지 않는 경우 catch / catch에 규칙적인 코드를 넣으면 try / catch가 잘못 사용됩니다. 여기에 if / else 또는 이와 유사한 것이 필요합니다.
robsch

18

ES6 답변, 철저한 테스트 :)

const propExists = (obj, path) => {
    return !!path.split('.').reduce((obj, prop) => {
        return obj && obj[prop] ? obj[prop] : undefined;
    }, obj)
}

전체 테스트 범위의 Codepen 참조


테스트에서 플랫 소품의 값을 0으로 설정하지 못했습니다. 유형 강제에주의해야합니다.
germain

@germain 이것이 당신을 위해 작동 합니까 ? (나는 ===다른 falsys를 명시 적으로 비교 하고 테스트를 추가했습니다. 더 좋은 아이디어가 있으면 알려주세요).
Frank Nocke

플랫 소품의 값을 (으)로 설정하여 테스트에 다시 실패했습니다 false. 그런 다음 객체의 값을로 설정하고 싶을 수도 있습니다 undefined(이상하다는 것을 알고 있지만 JS입니다). 나는 양의 거짓 값을 다음과 'Prop not Found'같이 설정했다 .const hasTruthyProp = prop => prop === 'Prop not found' ? false : true const path = obj => path => path.reduce((obj, prop) => { return obj && obj.hasOwnProperty(prop) ? obj[prop] : 'Prop not found' }, obj) const myFunc = compose(hasTruthyProp, path(obj))
germain

코드 펜 (오른쪽 위, 쉬운)을 포크하고, 테스트를 수정 및 추가하고, URL을 보내 주시겠습니까? 감사합니다 =)
Frank Nocke

(거대한) 타사 라이브러리로 도망가는 것은 가능하지만 내 취향은 아닙니다.
Frank Nocke

17

또한 babel 7- tc39-proposal-optional-chaining 과 함께 tc39 옵션 체인 제안을 사용할 수 있습니다

코드는 다음과 같습니다.

  const test = test?.level1?.level2?.level3;
  if (test) alert(test);

일부 TC39 멤버는 이의를 제기하므로이 구문은 거의 확실히 변경 될 것입니다.
jhpratt GOFUNDME 라이센스 갱신

아마도 이것은 시간이 지남에 따라 형태로 제공 될 것입니다. 그것이 중요한 것은 .. JS에서 가장 많이 놓친 기능 중 하나입니다.
Goran.it

11

나는 재귀 접근법을 시도했다.

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';

9

다음 스크립트는 더 읽기 쉬운 표현을 제공한다고 생각합니다.

함수를 선언하십시오 :

var o = function(obj) { return obj || {};};

다음과 같이 사용하십시오 :

if (o(o(o(o(test).level1).level2).level3)
{

}

부호 o를 사용하고 있기 때문에 "슬픈 광대 기술"이라고 부릅니다.


편집하다:

다음은 TypeScript 버전입니다

컴파일 타임에 유형 검사를 제공합니다 (Visual Studio와 같은 도구를 사용하는 경우 인텔리전스뿐만 아니라)

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")

1
°0o <°(())))><
Daniel W.

1
나는 이것이 정직하고 당신이 당신의 Object타입을 모른다면 당신의 얼굴에 "정의되지 않은"을 던지기 때문에 이것을 좋아 합니다. +1.

1
문장을 괄호 안에두면 행복 광대 기술이라고 할 수 있습니다 (o
Sventies

감사합니다. 나는 당신의 의견을 사랑합니다. 보기에는 꽤 좋은 각도입니다. 이러한 조건은 주로 "if"에서 사용되며 항상 외부 괄호로 둘러싸여 있습니다. 그래서, 그렇습니다, 그것은 대부분 행복한 광대입니다 :)))
VeganHunter

당신은 정말로 이것을 위해 괄호와 사랑에 있어야합니다 ...
Bastien7

7

글로벌을 만들고 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))

조건이라면

if(isExist(()=>obj.test.foo)){
   ....
}

잘 작동합니다. 간단하고 효율적
gbland777

6

간단한 방법은 다음과 같습니다.

try {
    alert(test.level1.level2.level3);
} catch(e) {
    alert("undefined");    // this is optional to put any output here
}

(가) try/catch보다 높은 레벨의 임의의 테스트 test.level1 등의 오브젝트들에 대한 예를 잡는다 test.level1.level2 정의되지 않는다.


6

프록시를 사용하는 사람의 예를 보지 못했습니다.

그래서 나는 내 자신을 생각해 냈습니다. 그것에 대한 좋은 점은 문자열을 보간 할 필요가 없다는 것입니다. 실제로 체인 가능 객체 함수를 반환하고 마술로 할 수 있습니다. 심층 객체를 확인하기 위해 함수를 호출하고 배열 인덱스를 가져올 수도 있습니다.

위의 코드는 동기식으로 잘 작동합니다. 그러나이 아약스 호출과 같은 비동기식을 어떻게 테스트 할 것입니까? 그것을 어떻게 테스트합니까? 500 http 오류를 반환 할 때 응답이 json이 아닌 경우 어떻게합니까?

window.fetch('https://httpbin.org/get')
.then(function(response) {
  return response.json()
})
.then(function(json) {
  console.log(json.headers['User-Agent'])
})

async / await를 사용하여 일부 콜백을 제거 할 수 있는지 확인하십시오. 그러나 더 마술처럼 할 수 있다면 어떨까요? 다음과 같은 것 :

fetch('https://httpbin.org/get').json().headers['User-Agent']

아마도 모든 약속과 .then사슬이 어디에 있는지 궁금 할 것입니다 ...이 모든 것을 막을 수 있습니다 ... 그러나 같은 프록시 기술을 약속과 함께 사용하면 단일 함수를 작성하지 않고도 실제로 중첩 된 복잡한 경로를 테스트 할 수 있습니다


누군가 관심이 있다면, npm에
Endless

5

이 답변을 바탕으로 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 );
}

1
마지막 접근 방식은 다음과 같습니다function validChain (object, path) { return path.split('.').reduce((a, b) => (a || { })[b], object) !== undefined }
James Harrington

5

중첩 된 객체 속성을 안전하게 가져 오는 작은 기능을 만들었습니다.

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

내가 시도한 문서와 테스트가있는 모든 코드를 보려면 내 github 요점을 확인 하십시오.


4

@CMS 우수 답변의 더 짧은 ES5 버전 :

// 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

이것에 대한 유일한 문제는 여러 수준의 정의되지 않은 키가있는 경우 TypeError를 얻는 것입니다.checkObjHasKeys(test, ['level1', 'level2', 'asdf', 'asdf']);
JKS

1
보다 적합한 방법은 every 이며 값을 직접 반환 할 수 있습니다.
RobG

로 변경 success = false;될 수 return false있습니다. 일단 그것이 깨 졌다는 것을 알고 나면 구제되어야하며, 일단 null이거나 정의되지 않으면 더 깊은 것은 존재할 수 없습니다. 이렇게하면 더 깊은 중첩 항목의 오류가 발생하지 않습니다.
웨이드

4

속성이 존재하면 반환 될 값을 찾고 있었으므로 위의 CMS로 답변을 수정했습니다. 내가 생각해 낸 것은 다음과 같습니다.

function getNestedProperty(obj, key) {
  // Get property array from key string
  var properties = key.split(".");

  // Iterate through properties, returning undefined if object is null or property doesn't exist
  for (var i = 0; i < properties.length; i++) {
    if (!obj || !obj.hasOwnProperty(properties[i])) {
      return;
    }
    obj = obj[properties[i]];
  }

  // Nested property found, so return the value
  return obj;
}


Usage:

getNestedProperty(test, "level1.level2.level3") // "level3"
getNestedProperty(test, "level1.level2.foo") // undefined


3

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

3

이 답변 에서 시작하여 다음 옵션을 자세히 설명했습니다 . 둘 다 같은 트리 :

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

정의되지 않은 검색 중지

var u = undefined;
o.a ? o.a.b ? o.a.b.c : u : u // 1
o.x ? o.x.y ? o.x.y.z : u : u // undefined
(o = o.a) ? (o = o.b) ? o.c : u : u // 1

각 레벨을 하나씩 확인하십시오

var $ = function (empty) {
    return function (node) {
        return node || empty;
    };
}({});

$($(o.a).b).c // 1
$($(o.x).y).z // undefined

3

나는이 질문이 오래되었다는 것을 알고 있지만 이것을 모든 객체에 추가하여 확장 기능을 제공하고 싶었습니다. 사람들이 확장 객체 기능을 위해 Object 프로토 타입을 사용하는 것에 대해 눈살을 찌푸리는 경향이 있다는 것을 알고 있지만, 이렇게하는 것보다 쉬운 것은 없습니다. 또한 이제 Object.defineProperty 메서드를 사용할 수 있습니다.

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") )

여기 jsfiddle이 있습니다. 테스트 할 이며, 특히 속성을 열거 할 수 있기 때문에 Object.prototype을 직접 수정하면 중단되는 일부 jQuery가 포함됩니다. 이것은 타사 라이브러리에서 잘 작동합니다.


3

나는 이것이 약간의 개선이라고 생각합니다 (1 라이너가 됨).

   alert( test.level1 && test.level1.level2 && test.level1.level2.level3 )

이는 && 연산자가 평가 한 최종 피연산자 (및 단락)를 반환하기 때문에 작동합니다.


당신은 말 그대로 그들이하는 말을 그대로 복사하고 피하고 싶습니다.
Sean Kendle

3

이것은 모든 객체와 배열에서 작동합니다 :)

전의:

if( obj._has( "something.['deep']['under'][1][0].item" ) ) {
    //do something
}

이것은 Brian의 대답의 개선 된 버전입니다.

기존 has 속성과 충돌 할 수 있으므로 _has 를 속성 이름으로 사용 했습니다 (예 : 맵).

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

여기 바이올린이 있습니다


3

여기에 내가 취할 수있는 조치가 있습니다. 이러한 솔루션의 대부분은 다음과 같이 중첩 배열의 경우를 무시합니다.

    obj = {
        "l1":"something",
        "l2":[{k:0},{k:1}],
        "l3":{
            "subL":"hello"
        }
    }

확인하고 싶을 수도 있습니다 obj.l2[0].k

아래 기능으로 할 수 있습니다 deeptest('l2[0].k',obj)

객체가 존재하면 함수는 true를, 그렇지 않으면 false를 반환합니다.

function deeptest(keyPath, testObj) {
    var obj;

    keyPath = keyPath.split('.')
    var cKey = keyPath.shift();

    function get(pObj, pKey) {
        var bracketStart, bracketEnd, o;

        bracketStart = pKey.indexOf("[");
        if (bracketStart > -1) { //check for nested arrays
            bracketEnd = pKey.indexOf("]");
            var arrIndex = pKey.substr(bracketStart + 1, bracketEnd - bracketStart - 1);
            pKey = pKey.substr(0, bracketStart);
			var n = pObj[pKey];
            o = n? n[arrIndex] : undefined;

        } else {
            o = pObj[pKey];
        }
        return o;
    }

    obj = get(testObj, cKey);
    while (obj && keyPath.length) {
        obj = get(obj, keyPath.shift());
    }
    return typeof(obj) !== 'undefined';
}

var obj = {
    "l1":"level1",
    "arr1":[
        {"k":0},
        {"k":1},
        {"k":2}
    ],
    "sub": {
       	"a":"letter A",
        "b":"letter B"
    }
};
console.log("l1: " + deeptest("l1",obj));
console.log("arr1[0]: " + deeptest("arr1[0]",obj));
console.log("arr1[1].k: " + deeptest("arr1[1].k",obj));
console.log("arr1[1].j: " + deeptest("arr1[1].j",obj));
console.log("arr1[3]: " + deeptest("arr1[3]",obj));
console.log("arr2: " + deeptest("arr2",obj));


3

이제 reduce중첩 키를 반복 하는 데 사용할 수도 있습니다 .

// @params o<object>
// @params path<string> expects 'obj.prop1.prop2.prop3'
// returns: obj[path] value or 'false' if prop doesn't exist

const objPropIfExists = o => path => {
  const levels = path.split('.');
  const res = (levels.length > 0) 
    ? levels.reduce((a, c) => a[c] || 0, o)
    : o[path];
  return (!!res) ? res : false
}

const obj = {
  name: 'Name',
  sys: { country: 'AU' },
  main: { temp: '34', temp_min: '13' },
  visibility: '35%'
}

const exists = objPropIfExists(obj)('main.temp')
const doesntExist = objPropIfExists(obj)('main.temp.foo.bar.baz')

console.log(exists, doesntExist)


3

재귀 함수를 사용하여이를 수행 할 수 있습니다. 중첩 된 모든 객체 키 이름을 모르더라도 작동합니다.

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

2

안전한 방법으로 코드 보드 (safeRead)에 기능이 있습니다.

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

속성이 null이거나 정의되지 않은 경우 빈 문자열이 반환됩니다.


설정하지 않으면 빈 문자열을 반환하기 때문에 템플릿과 함께이 방법을 좋아합니다.
Lounge9

2

이전 주석을 기반으로 다음은 주 객체를 정의 할 수없는 다른 버전입니다.

// Supposing that our property is at first.second.third.property:
var property = (((typeof first !== 'undefined' ? first : {}).second || {}).third || {}).property;

2

원하는 경로를 취하고 좋고 나쁜 콜백 함수가있는 내 자신의 함수를 작성했습니다.

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

용법:

var testObject = {
    level1:{
        level2:{
            level3:{
            }
        }
    }
};


checkForPathInObject(testObject, "level1.level2.level3", function(){alert("good!")}, function(){alert("bad!")}); // good

checkForPathInObject(testObject, "level1.level2.level3.levelNotThere", function(){alert("good!")}, function(){alert("bad!")}); //bad

나는 당신의 코드를 내 대답
davewoodhall

2
//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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.