programming language/javascript

[javascript] 유용한 js 기능 - 2 / generator 함수 / 날짜 변환

공대키메라 2022. 6. 5. 23:17

이번에는 저번에 글에 이어서 (저번 글 보고 싶다면 클릭!)

 

1. 유용한 js기능을 조금 더 소개하고

2. es6에서 도입된 generator 함수에 대해 알아보고

3. 또 날짜 변환을 공부할 것이다. 

 

이 글을 정리하기 위해 참고한 사이트는 다음과 같다. 

 

참고 : 

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

https://blog.bitsrc.io/2-3-javascript-tricks-that-you-should-know-bfb9827fd835

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#%EA%B0%99%EC%9D%B4_%EB%B3%B4%EA%B8%B0

https://sunlightmedia.org/ko/%EC%9E%90%EB%B0%94-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%8C%81/

https://www.youtube.com/watch?v=qi24UqyJLgs 

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Generator

https://bbaktaeho-95.tistory.com/80

1. 유용한 js 기능

1-1.!! 연산자(operator)를 이용해  boolean값을 반환하라!

!!는 이중 부정 연산자인데 0, '', "", null, NaN, undefined의 경우에는 false를 반환하고 그 외의 경우에는 true를 반환한다. 

예시

console.log(!!0) //  false
console.log(!!"") //  false
console.log(!!null) //  false
console.log(!!NaN) //  false
console.log(!!undefined) //  false
console.log(!!'test') //  true

 

우리가 어떤 함수를 만들었을 때, 그 값이 없는 경우가 간혹 있다.

 

위의 경우에 이를 이용해서 분기 처리를 하면 유용할 것 같다.

 

1-2. 구조 분해 할당(Destructing assignment) 사용하기

구조 분해 할당은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JS 표준식이다.

 

구조 분해 할당은 배열 구조 분해, 객체 구조 분해가 있다.

배열 구조 분해 할당

[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [30, 40, 50]

객체 구조 분해 할당

let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}
console.log(a); // 10
console.log(b); // 20
console.log(rest); // { c: 30, d: 40 }

 

여기서 배열 내에서 변수들의 위치를 변경하고 싶을 때 활용할 수 있다. 

 

let [a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [30, 40, 50]

let reuslt = [...rest, b, a] // [30, 40, 50]
console.log(reuslt) // [30, 40, 50, 20, 10]

 

근데 설마 IE로 서비스를 제공해야 한다면... 안타깝지만... ㅠㅠ

출처 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

 

진짜...Microsoft 는 반성해라... IE도대체 되는게 뭐냐? 후... 당장 사라져버렷!

1-3. reduce 사용하기

js에는 좋은 내장 기능들이 많은데 이것을들 아직 잘 활용하지 못하고 있다.

 

이번 기회에 reduce도 공부를 해서 써먹어보겠다.

 

reduce() 메서드는 배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환한다.

 

리듀서 함수는 네 개의 인자를 가진다.

  1. 누산기 (acc) -> accumulator
  2. 현재 값 (cur) -> currentValue
  3. 현재 인덱스 (idx) -> currentIndex
  4. 원본 배열 (src) -> array

리듀서 함수의 반환 값은 누산기에 할당되고, 누산기는 순회 중 유지되므로 결국 최종 결과는 하나의 값이 된다.

 

다음을 예시로 보자.

예시

[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) {
  console.log(accumulator);
  console.log(currentValue);
  console.log(currentIndex);
  console.log(array);
  console.log('------------------------------------')
  return accumulator + currentValue;
});

 

결과값 클릭!

 

더보기
0
1
1
[0, 1, 2, 3, 4]
"------------------------------------"
1
2
2
[0, 1, 2, 3, 4]
"------------------------------------"
3
3
3
[0, 1, 2, 3, 4]
"------------------------------------"
6
4
4
[0, 1, 2, 3, 4]
"------------------------------------"

 

다음과 같이 또 선언이 가능하다. 배열도 넘겨줄 수 있고, 객체도 넘겨줄 수 있다.

예시

console.log([0, 1, 2, 3, 4].reduce( (prev, curr) => prev + curr,10 ));
// 출력 : 20
// initialValue를 맨 뒤에 줄 수 있다.

var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(function (accumulator, currentValue) {
    return accumulator + currentValue.x;
},initialValue)

console.log(sum) // logs 6

 

이번에는 객체로도 전달이 가능하다.

예시

let names = ['shanes', 'doctor kim', 'bangal tiger', 'msg', 'isac toast'];

let countedNames = names.reduce(function (allNames, name) {
  if (name in allNames) {
    allNames[name]++;
  }
  else {
    allNames[name] = 1;
  }
  return allNames;
}, {});

console.log(countedNames)

 

accumulator라는 부분이 결국 단순 숫자 뿐만 아니라 우리가 어떤 값을 return해주는지에 따라 결과값이 차곡 차곡 쌓이는 부분인 것이다. 

 

위의 예제 같은 경우에는 names에 해당되는 이름이 name으로 하나씩 looping이 될 때 들어오는데 초기 inivialValue가 빈 객체이기 때문에 객체에 key, value형식으로 들어가는 것이다. 

1-4. 객체 복사하기

바로 다음 코드를 한번 보자. 

 

const result = {result:'이힝~ 동일하지롱~'}
const result1 = result

console.log(result)// {result:'이힝~ 동일하지롱~'}
console.log(result1)// {result:'이힝~ 동일하지롱~'}
result.data='added'
console.log(result)// {result:'이힝~ 동일하지롱~', data:'added'}
console.log(result1)// {result:'이힝~ 동일하지롱~', data:'added'}
console.log(result === result1)

 

내가 원하는 것은 객체의 값을 값만 복사해서 새롭게 사용하고 싶엇는데

 

얕은 복사가 일어나서 현재 원본 객체도 영향을 받는 상황이다. 

 

필자가 원하는것은 깊은 복사인데 그러려면 어떻게 해야 하나?

 

방법 1. JSON.parse & JSON.stringify 를 이용한 깊은 복사

const result2 = {result:'이힝~ 동일하지롱~'}
const result3 = JSON.parse(JSON.stringify(result2))

console.log(result2)// {result:'이힝~ 동일하지롱~'}
console.log(result3)// {result:'이힝~ 동일하지롱~'}
result3.data='new added'
console.log(result2)// {result:'이힝~ 동일하지롱~'}
console.log(result3)// {result:'이힝~ 동일하지롱~', data:'new added'}
console.log(result2 === result3)

방법 2. 재귀 함수를 이용한 깊은 복사

function deepCopy(object) 
    {  if (object === null || typeof object !== "object") {    
        return object; 
    }  // 객체인지 배열인지 판단  
    const copy = Array.isArray(object) ? [] : {};   

    for (let key of Object.keys(object)) {    
        copy[key] = deepCopy(object[key]);  
    }   
        return copy;
}
출처: https://bbangson.tistory.com/78 [뺑슨 개발 블로그:티스토리]

방법 3. Object.assign()을 활용한 깊은 복사

let objects = { 'a': 1 };
 
let deep = Object.assign({},objects);

console.log(objects) // { 'a': 1 }
console.log(deep) // { 'a': 1 }
deep.data='added'
console.log(objects) // { 'a': 1 }
console.log(deep) // { 'a': 1 , data : 'added'}

 

참고로 빈 객체를 비교하면 어떤 결과값이 나오는지 아시나요? (아시나요 먹고싶다)

console.log({} === {}) // false

 

위에서 Object.assign은 결국에 그냥 새로운 빈 객체를 생성해서 기존에 참고하고자 하는 객체의 데이터를 넣어주는 것이다. 

 

이것 때문에 회사에서 잠시 고민을 했었기에 한번 정리를 했다.

 

이건 별개의 이야기지만 node.js로 프레임워크를 만드는데 내부에서 하나의 객체로 결과값을 세팅하게끔 되어 있었다. 

 

그렇게 하면 위와 같이 얕은 복사와 깊은 복사를 고려해야 하는 상황이 올 수 도 있는데 그렇게 한 것이 좀 이해가 안된다.

 

하여간.... 그래서 정리를 해보았다.

2. genorator사용하기

generator객체는 genorator function 으로부터 반환된 값이며 이터러블 프로토콜과 이터레이터 프로토콜을 준수한다. 

 

우리가 함수를 선언할 때 function 옆에 '*' 표시를 붙여주면 generator함수가 된다. 

 

genorator라는 단어를 보게 되면 어떤 생각이 드는가?

 

필자는 보자마자 생성자, 발전기 등의 느낌으로 다가왔다. 

 

원하는 값을 호출할 때 마다 생성해 낸다는 것이다. 

 

genorator 에는 yield라는 keyword를 사용하는데 이 키워드는 genorator 함수가 호출 될 경우 그 곳에서 멈추게 된다.

 

yield의 뜻을 생각해보면 항복한다 라는 뜻을 가지고 있다. 

 

yield라는 단어는 이미 다른 분야에서 통용되고 있는 것으로 고체 역학 관점에서는 물체의 영구 변형이 명백한 지점을  yield point(항복점)라고 부른다. 

 

마찬가지로 어떤 작업이 멈추게 된다 (항복!) 한다는 의미로 yield라고 keyword를 만들어준게 아닌가 한다. (내 뇌피셜...)

예시 1

 

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

let g = gen(); // "Generator { }"

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

예시 2

function* fn() {
	const num1 = yield "첫번째 숫자를 입력해주세요";
    console.log(num1);
    
    const num2 = yield "두번째 숫자를 입력해주세요";
    console.log(num2)
    
    return num1 + num2;
}

const a = fn();
console.log(a.next()); // {done: false, value: "첫번째 숫자를 입력해주세요"}
console.log(a.next(2)); // {done: false, value: "두번째 숫자를 입력해주세요"}
console.log(a.next(3)); // {done: true,value: 5}

 

이렇게 변수도 전달해서 사용할 수가 있다.

예시 3

function* fn(){
    var index = 0;
    while(true)
        yield index++;
}

let gen = fn(); // "Generator { }"

console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
console.log(gen.next().value); // 4
console.log(gen.next().value); // 5

 

이번에는 무한으로 선언할 때 마다 값을 만들어낼 수 있다.

 

이렇게 보면 generator를 사용하면 현재는 값이 없엇는데 새로운 값을 호출시 만들어 주는 것을 지연 평가라고 한다. 

 

근데 이걸 도대체 어디에 적용해야 좋은지 필자는 잘 모르겠다. 

 

이에 대해서는 별도의 한 섹션으로 이야기를 해야 할 만큼 무언가 많은 장점이 있는 것 같다. 

 

다음에 js의 generator에 대해 따로 설명을 하겠다.

3.날짜 변환

js에서 날짜를 계산할 일이 많았는데 필자는 js에서 제공하는 날짜 함수를 사용하면서 착각으로 인한 버그를 양산해냈다.

 

이번에는 크롬 개발자 도구를 이용해서 테스트하려고 한다. 

예시

const result = new Date()
console.log(result)
console.log(result.getTime())

const result2 = new Date(result.getTime())
console.log(result2.getFullYear())
console.log(result2.getMonth()-5)

const makeStr = `${result2.getFullYear()}년 ${result.getMonth()}월`
console.log(makeStr) // "2022년 5월"

const makeStr1 = `${result2.getFullYear()}년 ${result.getMonth()-5}월`
console.log(makeStr1)  // "2022년 0월"

 

예시를 보면 뭔가 이상한게 있다. 

 

아니... 2022년 0월은 뭐야 도대체?

 

이것이 필자가 겪은 버그인데 js에서는 날짜의 month를 0부터 11까지 1월 에서 12월로 생각한다. 

 

그래서 원래 2022년 1월을 출력하고 싶었는데, 반복문을 이용해서 날짜를 만들어서 보여주려다가 0월인 경우가 생긴 것이다....  ㅠㅠ 그걸로 30분 정도 순삭해버렸다.... 

 

이것은 현재 기억이 안나서 내가 작성한 코드를 참고해서 다시 올려서 정리하도록 하겠다.