728x90

Set Object in JS

Set은 Javascript 표준 내장 객체로, 원시값(primitive)과 객체 참조 모두 유일한 값을 저장할 수 있다.

Array와 다르게 중복된 값을 허용하지 않는다.

 

const set = new Set();

console.log(typeof set); // object
console.log(set); // Set(0){ size : 0 }

 

Set의 type은 object이며, Set의 내장 메서드는 아래와 같다.

  1. Set.prototype.add() : set에 값 추가한다.
  2. Set.prototype.clear() : set을 비워준다.
  3. Set.prototype.delet() : set에서 특정 값을 삭제한다.
  4. Set.prototype.entries() 
  5. Set.prototype.forEach()
  6. Set.prototype.has()
  7. Set.prototype.keys()
  8. Set.prototype.values()

Set.prototype.add(value) : Set 값 추가하기

const set = new Set();
set.add(1);
set.add('string');
set.add(['a','r','r','a','y']);
set.add({ "0" : "object" });

console.log(set); // Set(4) { 1, 'string', Array(5), {...} }

set에는 객체(object), 배열(array), 숫자(number), 문자열(string)등 다양한 값을 추가할 수 있다.

 

Set.prototype.clear() : Set 비워주기

const set = new Set();
set.add(1);
set.add(2);

console.log(set); // Set(2) { 1, 2 }

set.clear();
console.log(set); // Set(0) {size : 0}

 

Set.prototype.delete(value) : Set  특정 값 삭제하기

return false or true ( 삭제 성공 시 true, 삭제 실패 시 false )

const set = new Set();
set.add(1);
set.add('string');
set.add(['a','r','r','a','y']);
set.add({ "0" : "object" });

console.log(set); // Set(4) { 1, 'string', Array(5), {...} }

set.delete("string"); // true
set.delete("string"); // false

console.log(set); // Set(3) { 1, Array(5), {...} }

 

Set.prototype.entries() : Set 순회하기

entries() method는 Set 객체의 각각의 요소를 삽입순서대로 [ 값, 값 ]의 형태로 가진 배열의 새로운 iterator 객체를 반환한다.

Set 객체에는 Map 객체처럼 Key가 존재하지 않는다. 그러나 Map 객체의 API와 비슷하게 유지하기 위해, 각각의 "요소"는 "키"와 "값"자리에 같은 값을 가지게 된다. 결과적으로 entries는 [ 값, 값 ] 형태의 배열을 반환한다. 

 

const set = new Set();

set.add(1);
set.add(2);
set.add(3);

const iterator = set.entries();

console.log(iterator);
// output : SetIterator {1 => 1, 2 => 2, 3 => 3}

for(const entry of iterator ){
	console.log(entry);
}

// output 
// [1, 1]
// [2, 2]
// [3, 3]

console.log(iterator); // SetIterator {}
// output : undefined

for .. of .. 문법으로 entries() 함수로 반환된 iterator를 순회하면서 console.log()로 찍어본 결과 [ 값, 값 ] 의 형태로 set에 추가했던 1, 2, 3이 순서대로 나오는 것을 확인할 수 있다. 

이후 iterator를 다시 console.log()로 확인해보면 해당 iterator가 비어있는 것을 확인할 수 있다.

이것은 iteration protocols 를 확인해보면 더 자세히 알 수 있다.

쉽게 생각하면 iterator는 한번 가리켰던 value는 다음 value를 가리키게 되는 순간 다시 바라볼 수 없다. 즉 한번 순회하면 다시 반복할 수 없는 것이다.

 

 iterator에 우리가 set에 값을 넣은 순서대로 [1,1], [2,2], [3,3]이 들어있다. 처음에 iterator는 [1,1]을 가리키고있다. 다음에 [2,2]를 가리키게 되면 iterator에서 [1,1]은 사라지고 [2,2], [3,3]만 남아있다. 

아래 코드를 통해서 확인할 수 있다.

const set = new Set();
set.add(1);
set.add(2);
set.add(3);

for(const entry of iterator){
    console.log(entry);
    console.log(iterator);
}

// output
// [1,1]
// SetIterator {2 => 2, 3 => 3}

// [2,2]
// SetIterator {3 => 3}

// [3,3]
// SetIterator {}

 

Set.prototype.forEach(callbackFunction) : Set 반복하기

set의 forEach method는 Array의 forEach method와 동일하다.

set의 value들을 순차적으로 한번씩 순회한다. forEach대신 위에서 사용했던 for..of를 사용해서도 set을 반복할 수 있다.

const set = new Set();

set.add(1);
set.add(2);
set.add(3);

set.forEach((s)=>{
	console.log(s);
});

// output : 
// 1
// 2
// 3

for(const entry of set){
    console.log(entry);
}

// output : 
// 1
// 2
// 3

 

Set.prototype.has(value) : Set에 특정 value 존재 여부 확인하기

Set 객체에 특정 value가 존재하는지 확인하기 위한 함수이다. 특정 value가 존재한다면 true, 존재하지 않는다면 false를 반환한다.

const set = new Set();
set.add(1);
set.add(2);
set.add(3);

console.log(set.has(1)); // true
console.log(set.has(4)); // false

 

Set.prototype.values() : Set의 value iterator 반환한다. 

Set 객체의 value들에 대한 iterator를 반환한다.

const set = new Set();
set.add(1);
set.add(2);
set.add(3);

let valueIterator = set.values();

console.log(valueIterator); // SetIterator {1, 2, 3}

for(const entry of valueIterator){
	console.log(entry);
}

// output
// 1
// 2
// 3

valueIterator = set.values();

console.log(valueIterator.next()); // {value: 1, done: false}
console.log(valueIterator.next()); // {value: 2, done: false}
console.log(valueIterator.next()); // {value: 3, done: false}
console.log(valueIterator.next()); // {value: undefined, done: true}

next()는 iterator의 method이다. 참고

 

Set.prototype.keys() : Set의 key iterator 반환한다. 

❗️Set object에서는 keys() 메서드와 values() 메서드는 동일한 기능을 한다. 왜냐하면 Set 객체의 경우 value 값을 키값으로 사용하기 때문에 [ key, value ] 쌍의 값으 [ value, value]와 동일하기 때문이다.

const set = new Set();
set.add(1);
set.add(2);
set.add(3);

console.log(set.keys());
// output : SetIterator {1, 2, 3, 'string'}
728x90

NaN 이란 무엇인가?

NaN은 Not a Number ( 숫자가 아님 )을 나타낸다.

이번에 인턴일로 맡은 작업에서 NaN을 되게 많이 맞닥뜨렸는데, NaN이 무엇을 의미하는지 명확하게 몰랐었어서 난감했었다.

이번에 NaN으로 고생하기 전까지만해도 NaN의 개념을 undefiend나 null과 비슷한 개념이라고 생각을 하고 있었고, NaN인지 판단하는 로직 또한 일반적인 === (Strict equality) 연산자로 비교할 수 있는 줄 알았다.

 

내가 이번 프로젝트에서 에러사항을 겪었던 로직과 유사하게 코드를 작성해 보았다.

아래 코드의 결과는 무엇일까?

나처럼 NaN이 무엇인지 명확하게 모르거나 NaN을 처음 접해보는 사람은 당연히 "a is not number"가 출력될 것이라고 생각할 것이다.

( 아님 말고 ! )

const a = NaN;

if( a === NaN || a === undefined || a === '0' ) {
	console.log("a is not number");
}
else{
	console.log("a is number");
}

그러나 이 코드를 실제로 돌려보면 아래와 같이 "a is number"라는 결과가 나오는 것을 알 수 있다.

왜 이러한 결과가 나오는 것일까 ? 내가 원하는 결과는 "a is not number"인데 !!

처음에는 | a === NaN | 비교 구문이 문제가 있다는 것을 파악하지 못했어서 조금 시간이 걸렸었다.

디버깅 하면서 NaN을 비교하는 구문이 제대로 실행되지 않았다는 것을 파악하고, NaN에 대해 먼저 구글링해보았다.

 

MDN 문서에 따르면 NaN은 전역 스코프의 변수로, NaN의 초기값은 Not-A-Number로, Number.NaN의 값과 같다고 한다.

최신 브라우저에서 NaN은 설정 불가, 쓰기 불가 속성이라고 한다. 

 

NaN 판별

NaN은 다른 모든 값과 비교 ( == , !=, ===, !== )했을 때 같지 않으며(false), 다른 NaN과도 같지 않다.

NaN의 판별은 오로지 Number.isNaN() 또는 isNaN()을 사용해야 제일 분명하게 수행할 수 있다. 아니면 오로지 NaN만이 자기 자신과 비교했을 때 같지 않음을 이용할 수도 있다.

 

아래 예제를 통해 더 쉽게 이해할 수 있다.

NaN === NaN;        // false
Number.NaN === NaN; // false
isNaN(NaN);         // true
isNaN(Number.NaN);  // true

function valueIsNaN(v) { return v !== v; }
valueIsNaN(1);          // false
valueIsNaN(NaN);        // true
valueIsNaN(Number.NaN); // true

출처 : MDN

 

그러나 isNaN()과 Number.isNaN()의 차이점을 기억해 두어야 한다. isNaN은 현재 값이 NaN이거나, 숫자로 변환했을 때 NaN이 되면 참을 반환하지만, Number.isNaN은 현재 값이 NaN 이어야만 참을 반환한다.

const an = '8';
const nan = 'c';

console.log(isNaN(an)); // false
console.log(isNaN(nan)); // true

console.log(Number.isNaN(an)); // false
console.log(Number.isNaN(nan)); // false

 

728x90

VanillaJS를 이용하여 Tooltip Input Component 만들기

/**
 * (required) type : number | text
 * (required) id : text
 * (optional) blank : boolean
 * (optional) blankMessage : text
 * (optional) disabled: boolean
 * (optional) regExp : tooltip message view condition : regExp ( 양끝 / 제거한 값을 넣어줘야함 )
 * (optional) regExpMessage : tooltip message
 * (optional) frontLabel : text
 * (optional) backLabel : text
 * * */
class TooltipInput extends HTMLElement {
  /**
     * constructor : 최초 실행 생성 Method
     * */
  constructor() {
    super();
    this.render();
  }

  /**
     * render() : 해당 tag에 넣을 html tag를 rerendering한다.
     * */
  render() {
    this.innerHTML = this.getTemplate();
  }

  /**
     * 해당 컴포넌트가 mount될 때 불러진다.
     * * */
  connectedCallback() {
    const attrList = this.attributes;
    const input = this.getElementsByClassName('tooltip-input');

    let front = attrList.frontLabel;
    let back = attrList.backLabel;

    if (front === undefined) {
      front = this.getElementsByClassName('front');
      front[0].className = 'display-none';
    }
    if (back === undefined) {
      back = this.getElementsByClassName('back');
      back[0].className = 'display-none';
    }

    /**
         * input eventListener
         * * */
    input[0].addEventListener('input', (e) => {
      const { blank } = e.path[3].attributes;
      const { blankMessage } = e.path[3].attributes;
      const rex = e.path[3].attributes.regExp;
      const rexMessage = e.path[3].attributes.regExpMessage;

      /* input.value 빈칸인 경우 */
      if (e.currentTarget.value.length === 0) {
        if (blank !== null && blank !== undefined && (blank.value === 'true' || blank.value === '')) {
          const tooltipMessage = this.getElementsByClassName('tooltip-message');
          tooltipMessage[0].className = 'tooltip-message';
          if (blankMessage === undefined) {
            tooltipMessage[0].innerHTML = '값을 입력해주세요.';
          } else {
            tooltipMessage[0].innerHTML = blankMessage.value;
          }

          console.log(tooltipMessage[0].classList);
        }
      } else {
        /* input.value 빈칸이 아닌 경우 */
        /* attr 에 정규식이 있는 경우 */
        if (rex !== null && rex !== undefined) {
          const regExp = new RegExp(rex.value);
          console.log(!regExp.test(e.currentTarget.value));
          if (!regExp.test(e.currentTarget.value)) {
            // 정규식 만족 X
            const tooltipMessage = this.getElementsByClassName('tooltip-message');
            tooltipMessage[0].className = 'tooltip-message';
            if (rexMessage !== undefined && rexMessage !== null) {
              tooltipMessage[0].innerHTML = rexMessage.value;
            } else {
              tooltipMessage[0].innerHTML = '조건을 만족하지 않습니다.';
            }
          } else if (blank !== null && blank !== undefined && (blank.value === 'true' || blank.value === '')) {
            const tooltipMessage = this.getElementsByClassName('tooltip-message');
            tooltipMessage[0].className = 'tooltip-message display-none';
          }
        }
      }
    });

    /**
         * disabled 값이 존재하는지 체크
         * disabled 값이 존재 == true 인경우 input의 disabled attr 활성화
         * disabled 값이 존재하지 않거나 ( null ) false인 경우 input의 disabled attr 비활성화(삭제)
         * * */

    if (attrList.getNamedItem('disabled') !== null && (attrList.getNamedItem('disabled').value === 'true' || attrList.getNamedItem('disabled').value === '')) {
      /**
             * disabled 값이 true인 경우 input의 disabled attr 활성화
             * */
      input[0].setAttribute('disabled', 'disabled');
    }

    /**
         * input값이 빈칸인 경우 확인해야하는지 체크
         * blank === true && blankMessage === undefined => "입력값이 필요합니다."
         * blank === true && blankMessage !== undefined => "blankMessage"
         * * */

    if (attrList.getNamedItem('blank') !== null && (attrList.getNamedItem('blank').value === 'true' || attrList.getNamedItem('blank').value === 'blank')) {
      /**
             * blank === true
             * * */
      if (input[0].value === '') {
        const tooltipMessage = this.getElementsByClassName('tooltip-message');
        if (attrList.getNamedItem('blankMessage') !== null) {
          // blank === true && blankMessage === undefined => "입력값이 필요합니다."
          tooltipMessage[0].innerHTML = attrList.getNamedItem('blankMessage').value;
        } else {
          // blank === true && blankMessage !== undefined => "blankMessage"
          tooltipMessage[0].innerHTML = '입력값이 필요합니다.';
        }
      }
    }
  }

  /**
     * 해당 tag에 attributes를 observing한다.
     * * */
  static get observedAttributes() {
    return ['type', 'id', 'blank'];
  }

  /**
     * getTemplate() : rendering될 html tag에 대한 template이다.
     * * */
  getTemplate() {
    let frontLabel;
    let backLabel;

    const type = this.attributes.type.value || '';
    const id = this.attributes.id.value || '';

    frontLabel = this.attributes.frontLabel !== undefined ? this.attributes.frontLabel.value : '';
    backLabel = this.attributes.backLabel !== undefined ? this.attributes.backLabel.value : '';

    return `
            <style>
                input[type="number"]::-webkit-outer-spin-button,
                input[type="number"]::-webkit-inner-spin-button {
                    -webkit-appearance: none;
                    margin: 0;
                }
                
                div{
                    width : 100%;
                }
                
                .tooltip-input{
                    width : 100%;
                    height : 26px;
                    border-radius: 4px;
                    box-shadow: inset 0 3px 2px 0 rgba(49,49,49,0.13);
                    border : solid 1px #b4b4b4;
                    background: #fff;
                    padding-left : 12px;
                    
                    /* font */
                    font-style : normal;
                    font-size : 12px;
                    font-family: 'Roboto';
                    color : #6e6e6e;
                }
                
                .label-text{
                    font-style : normal;
                    font-size : 12px;
                    font-weight : 600;
                    font-family: 'Roboto';
                    color : #6e6e6e;
                }
                
                .disabled{
                    background: #e6e6e6;
                }
                
                .display-none{
                    display : none;
                }
                
                .display-block{
                    display: block;
                }
                
                .tooltip-message{
                    width : auto;
                    height : 16px;
                    border : 1px solid #ede381;
                    background: #f7f2c0;
                    color : #9d870e;
                    
                    white-space: nowrap;
                    
                    position : absolute;
                    right : -15px;
                    top : -32px;
                    padding : 7px 13px 7px 18px;
                    z-index : 30; 
                }
                
                .tooltip-box{
                    min-width : 50px;
                    display: block;
                    position : relative;
                }
                
                .tooltip-box-container{
                    width : 100%;
                    display: flex;
                    flex-direction: row;
                    align-items: center;
                }
                
                .front{
                    width : auto;
                    white-space: nowrap;
                    margin-right : 10px;
                }
                
                .back{
                    width : auto;
                    white-space: nowrap;
                    margin-left : 25px;
                }
                
            </style>
            
            <div class="tooltip-box-container">
                <label for="${id}" class="label-text front">${frontLabel}</label>
                <div class="tooltip-box">
                    <input id="${id}" type="${type}" class="tooltip-input"/>
                    <span class="tooltip-message display-none"></span>
                </div>
                <label for="${id}" class="label-text back">${backLabel}</label>
            </div>
        `;
  }

  /**
     * 해당 컴포넌트가 unmount될 때 불리어 진다.
     * * */
  disconnectedCallback() {

  }

  /**
     * attributes가 수정된다면 수정된 갯수만큼 불리어 진다.
     * * */
  attributeChangedCallback(name, oldValue, newValue) {

  }
}

/**
 * tool-tip 이라는 tag가 customElements에 있는가? 없다면 새롭게 define하라는 코드로,
 * 동일 tag가 2번 define하면 오류가 날 수 있으므로, component가 여러차례 사용되면서 define되는 것을 막기 위한 용도이다.
 * * */

// Define the new web component
if ('customElements' in window) {
  customElements.define('tooltip-input', TooltipInput);
}

과정과 더 자세한 설명은 다음에 쓸 예정

 

참고 블로그 링크 

 

'Language > Javascript' 카테고리의 다른 글

[ Javascript ] Set Object  (0) 2022.11.02
[ Javascript ] setTimeout, setInterval  (0) 2022.07.27
[ Javascript ] Array.prototype Methods  (0) 2022.03.08
[ Javascript ] async/await  (0) 2022.03.07
[ Javascript ] Promise  (0) 2022.03.06
728x90

문제 설명

문자열 s를 숫자로 변환한 결과를 반환하는 함수, solution을 완성하세요.

제한 조건
  • s의 길이는 1 이상 5이하입니다.
  • s의 맨앞에는 부호(+, -)가 올 수 있습니다.
  • s는 부호와 숫자로만 이루어져있습니다.
  • s는 "0"으로 시작하지 않습니다.
입출력 예

예를들어 str이 "1234"이면 1234를 반환하고, "-1234"이면 -1234를 반환하면 됩니다.
str은 부호(+,-)와 숫자로만 구성되어 있고, 잘못된 값이 입력되는 경우는 없습니다.


function solution(s) {
    return Number(s);
}

 

+ Recent posts