본문 바로가기

3.구현/HTML5&Javascript

[javascript] 문자열 키로 객체에 속성 존재 여부 확인하기

들어가기

객체 내에 특정 속성을 같은 값는 있는지 확인이 필요할 때가 있다. 속성을 지칭하는 키 길이 긴 경우에 확인하기 쉽지 않다. 예를 들어 "obj.data[1].item"이란 형태로 되어 있는 경우이다. 여기서는 해당 키가 문자열로 입력되고 특정 객체에서 문자열로 준 키에 해당하는 속성이 있는지 확인하는 방법을 소개하려고 한다.

작성자: ospace114@empal.com, http://ospace.tistory.com/

확인하는 방법

문자열로 된 키로 객체에 속성이 있는지 확인하는 방법이다. 예를 들어 다음과 같은 문자열 키들이다.

'info.name'
'info["name"]'
'data[0].item.value'
'data[0]["item"]["value"]'

방법은 단순하다. 위에 키들을 앞에서 순서대로 파싱해서 각각 속성 값을 추출하고 해당 속성이 있는지 검증한다.
실행결과는 키 값에서 존재하는 속성까지 표시된다. 아에 없다면 false가 된다.

function isalpha(c) {
  return ("a" <= c && c <= "z") || ("A" <= c && c <= "Z") || "_" === c;
}
function isnum(c) {
  return "0" <= c && c <= "9";
}
function hasProp(obj, prop) {
  let [i, c, step, b_str, begin, end] = [0, 0, 1, 0, 0, 0, 0];
  const keys = [];

  const checkProp = (l, r) => {
    if (l === r) return true;
    const key = prop.substring(l, r);
    if (!obj.hasOwnProperty(key)) return false;
    obj = obj[key];
    if ('function' === typeof obj) return false;
    keys.push(key);

    return true;
  };

  for (i = 0; i < prop.length; ++i) {
    c = prop.charAt(i);

    if (2 === step) {
      if ('"' === c || "'" === c) {
        ++begin;
        let b_str = c;
        ++i;
        for (; i < prop.length; ++i) {
          c = prop.charAt(i);
          if ("\\" == c) ++i;
          else if (b_str === c) break;
        }
        end = i;
        c = prop.charAt(++i);
      } else if (isnum(c)) {
        ++i;
        for (; i < prop.length; ++i) {
          c = prop.charAt(i);
          if (!isnum(c)) break;
        }
        end = i;
      }

      if ("]" === c) {
        step = 0;
      } else {
        throw Error(`invalid expression at ${i}: ${prop}`);
      }

      if (step) continue;
    }

    if (1 === step) {
      if (isalpha(c) || "$" === c || (i && isnum(c))) continue;
      if ("[" === c) {
        end = i;
        step = 2;
      } else if ("." === c) {
        end = i;
      }
    }

    if (!checkProp(begin, end)) {
      return keys.length ? keys.join(".") : false;
    }

    begin = end = i + 1;
    if (0 === step) step = 1;
  }

  if (!(0 === step || 1 === step)) {
    throw new Error(`invalid expression at ${i}: ${prop}`);
  }

  if (!checkProp(begin, i)) {
    return keys.length ? keys.join(".") : false;
  }

  return keys.length ? keys.join(".") : false;
}

한번 테스트해보자.

let obj = { info:{name: 'foo'}, data:[{item:{value:10}}], etc: undefined };

console.log(hasProp(obj, 'info.name'));
// info.name
console.log(hasProp(obj, 'info["name"]'));
// info.name
console.log(hasProp(obj, 'data[0].item.value'));
// data.0.item.value
console.log(hasProp(obj, 'data[0]["item"]["value"]'));
// data.0.item.value
console.log(hasProp(obj, 'data[1].item'));
// data
console.log(hasProp(obj, 'foo'));
// false

마침

필요하신 분이 있을 지 모르겠지만, 참고되시라고 올렸습니다. 모두 즐프하세요. ospace.

반응형