Land of Joe

23년 12월 14일 심화과정#1 Javascript ES6 문법 본문

📚 Educations/⏳ StageUs

23년 12월 14일 심화과정#1 Javascript ES6 문법

Arendt 2023. 12. 14. 20:33

 

 

그동안은 변수를 만들 때 var만 사용해왔다.

그러나 var의 문제점은 "중복선언이 가능"하고 "호이스팅이 된다"는 점이다. 

 

먼저 중복 선언의 예시

var를 통해 만든 num1은 위에서부터 아래로 읽는 js의 성격에 따라 서로 다른 값을 넣어준 것의 결과가 그대로 출력된다.

그러나 함수의 경우는 또 그렇지 않다는 게 이상한 점.

 

호이스팅(Hoisting)이란: 변수와 함수의 선언부분을 파일의 상단으로 끌고 올라오는 JS만의 편의기능이다.

위 중복 선언의 예시가 작동하는 순서대로 쓴 것이 아래의 이미지이다.

var num1 =10;에서 선언부는 var num1에 해당하고,

function printMessage() {console.log("hello")}는 전체가 선언부에 해당한다.

 

JS가 작동하는 순서에 따라 선언부를 위에 올리고,

아래에서 console.log나 함수 호출을 통해 출력하면 

함수의 두 번 호출로 값이 덮어씌워져 "bye"만 두 번 출력되게 된다. 

 

또한 var number = 30; 이라고 선언해주기 이전에 위에서 console.log(number)을 해버리면

선언이 안 되었기 때문에 error가 나야하는 것이 분명하나(JS를 제외한 나머지 언어에선 그러함)

호이스팅이라는 JS가 제공하는 편의기능에 의해 'undefined'가 출력되게 된다. 

 

but,,, 이러한 JS만의 편의기능이 대규모 프로젝트에서 유지보수를 망치는 주범이 되었다..!

 

so, 중복선언과 호이스팅을 해결하기 위해 명령어를 바꿔 사용하기로 한다. 

1. let, const

var 대신 let과 const를 사용한다.

 

let은 변수를 선언할 때 사용한다.

const는 상수를 선언할 때 사용한다. 한 번 값을 넣으면 절대 수정이 불가능하여 보통 함수를 만들 때 사용한다.

 

console.log(num1); 
let num1 = 10;

이렇게 먼저 출력을 하고 let을 통해 선언을 하면

=> Uncaught ReferenceError: Cannot access 'num1' before initialization 이라는 오류가 발생하게 된다. 

=> 초기화되기 전에 사용할 수 없다는 뜻이다.

 

2. 함수 표현식, 함수 람다식, 축약형

 

기존에 사용해왔던 것은 함수 선언식이다. 

// 함수 선언식
function test() {
console.log("test");
}

 

ES6부터는 함수 표현식과 함수 람다식, 그리고 람다식의 축약형이 제시되었다. 

// 함수 표현식 
let test = function() { 
      console.log("test")
}

 

함수 선언식에서 선언부는 function test () {} 전체였지만,

함수 표현식에서 선언부는 let test까지이다. 물론 선언에 let이 이용됐기 때문에 초기 선언 전에 위에서는 당연히 사용이 불가하다. 

 

대표적인 함수 선언식은 for문의 형태로, 함수 선언식이 how에 집중하는 느낌이라면,

함수 표현식은 what에 집중하는 느낌으로, 최근엔 표현식으로 쓰는 걸 더욱 선호하는 추세라고 한다. 

 

// 함수 람다식
let test = () => {
     console.log("test");
}

람다식은 그냥 표현식보다 훨씬 간결한 느낌

선언부 = () => {return }; 이게 전부이다. 

 

// 함수 람다식
let power = (number) => {
     return number * number
}
 
// 함수 람다식 축약형
let power = number => number * number 

const power = number => number * number

람다식에서 매개변수가 딱 하나일 때엔 또 축약을 할 수 있다.

매개변수가 들어가는 () 이 괄호도 없애주고, return도 없애준다. 

 

앞서 let과 const에서도 언급하였 듯, 함수를 만들 때엔 보통 상수(const)로 선언해주어 혹시 모를 사고를 방지하도록 한다. 

상황에 따라(for문 안에 있는 변수 설정 등의 경우) let을 이용한다. 

 

 

3. template literals

const name = "조경은"
 
console.log(`my name is ${name}.`)
 
// 기존
console.log("my name is " + name + ".");

기존엔 이렇게 더하기(+)와 큰따옴표를 이용했지만,

template literals 기법을 이용한다면 백틱(``) 사이에 원하는 문구를 넣고 변수를 넣고자 하는 곳에 ${ }를 써주기만 하면 된다. 

 

이 방법은 아래와 같이 간단한 연산도 집어넣을 수 있다는 것이 장점이다. 

const power = number => number * number
const number = 5
console.log(`${number}의 제곱은 ${power(number)}`)

=> 5의 제곱은 25이다.

 

 

4. default parameter

원의 넓이를 구하는 함수식을 함수 표현식으로 만들었다.

이때 매개변수로는 radius와 pie가 들어갔고

const circleOfArea = (radius, pie) => {
     return console.log(radius * radius * pie)
}
circleOfArea(5,3.14);

이렇게 하면 원하는 값이 출력될 것이다. 

 

그러나 매개변수에 기본값을 설정해주는 방법이 있다. 

const circleOfArea = (radius, pie=3.14) => {
     return console.log(radius * radius * pie)
}

이처럼 pie=3.14라는 기본값을 넣어주면

circleOfArea(5)

이렇게 기본값을 설정하지 않은 radius에 대한 값만 넣어주어도 결과값이 출력된다.

 

이는 함수의 유연성을 위해 나온 방법으로,

함수를 사용할 때에 기본값을 설정한 매개변수엔 별다른 지정을 안 해도 되지만,

원하는 때엔 새로운 값을 넣어도 되는 등, 매개변수에 여러 옵션을 주어 함수의 사용성을 높이는 데 도움을 준다. 

circleOfArea(5, 3.141592);

 

 

5. spread

list []나 object {}의 껍질을 벗기고 각 원소를 뿌려준다.

const list1 = [1,2,3,4,5]
console.log(list1);
console.log(...list1)

-> [1,2,3,4,5]

-> 1 2 3 4 5

 

const list1 = [1,2,3,4,5]
const list2 = [6,7,8]

const newList = [...list1, ...list2]
console.log(newList)

-> [1,2,3,4,5,6,7,8]

=> 각 리스트들, 각 오브젝트들을 합칠 때 사용하면 용이하다. 

 

 

6. Pass by Value, Pass by Reference

// Pass by Value
const number = 10
const convert = (value) => {
value += 10
}

console.log(number) // 10
 
convert(number)
console.log(number) // 함수를 거친 후의 변수 // 10 

console.log(convert(number)) // 20

=> 두번째 console.log(number)이 20이 아닌 10이 나오는 이유

: 함수에서는 number의 복사본에 대해서 작용하기 때문에 number는 바뀌지 않는다.

 

// Pass by Reference
const list = [1,2,3]
const convertList = (list) => {
     return list [0] += 10
}

console.log(list) // [1,2,3]
 
convertList(list)
console.log(list) // 함수를 거친 후의 list // [11,2,3]

 

=> list, object는 copy 값이 아닌, 주소값을 보낸다. 

 list, object는 변수가 아닌 자료구조이기 때문이다. 메모리에 올라간 특정한 주소값은 원본값과 공유되기 때문에 함께 변한다.

 

const list = [1,2,3]
const convertList = (list) => {
     return list [0] += 10
}
 
console.log(convertList([...list])) // 11
 
console.log(list) // [1,2,3]

[...list]

=> convertList 함수에 list 원본값이 아닌 복사본을 보낸 셈임

(적분했다가 다시 미분한 것과 같다)

=> 원본값과의 링크를 끊어줄 때 spread를 사용한다

 

 

 

7. Destructuring 재배열

const info = {
     "member": {
          "name": "조경은",
          "birth": "1999",
          "major": "경영"
     }
}

 

방법 1) 

const printObj = (obj) => {
     console.log(obj.member.name)
     console.log(obj.member.name)
     console.log(obj.member.name)
     console.log(obj.member.name)
     console.log(obj.member.name)
}

printObj(info)

 

방법 2)

const printObj = (obj) => {
     const name = obj.member.name
     const birth = obj.member.birth
     const major = obj.member.major
 
     console.log(obj.member.name)
     console.log(obj.member.name)
     console.log(obj.member.name)
     console.log(obj.member.name)
     console.log(obj.member.name)
}
 
printObj(info)

 

방법 3)

const printObj = (obj) => {
     const {name, birth, major} = obj.member
 
     console.log(obj.member.name)
     console.log(obj.member.name)
     console.log(obj.member.name)
     console.log(obj.member.name)
     console.log(obj.member.name)
}
 
printObj(info)

=> 매우 간단!

and, 해당 방법은 원본 링크와 끊어져 copy본이 생기게 되는 추가효과 또한 있다.

 

 

8. List Helper

리스트를 반복문과 함께 사용할 때 이를 간단하게 해주는 문법

 

8-1. forEach

list 길이만큼, list 안에 있는 요소 각자에 대해서 ~~를 하겠다.

const list = [1,2,3,4,5]
 
list.forEach((elem) => {
     console.log(elem) // 1 // 2 // 3 // 4 // 5
})
 
// 축약
list.forEach(elem => console.log(elem)) // 1 // 2 // 3 // 4 // 5
const list = [1,2,3,4,5]
let sum = 0;
 
list.forEach((elem) => {
     return sum += elem 
})
console.log(sum) // 15
 
// 축약
list.forEach(elem => sum + elem)
console.log(sum) // 15

 

8-2. map

list 안에 있는 요소 각자에 대해서 ~~를 해서/ 새로운 저장소에 리스트 형태로 저장해주겠다!

const newList = list.map((elem) => {
     return elem * elem 
})
 
console.log(newList) // [1,4,9,16,25]

// 축약
const newList = list.map(elem => elem * elem)

-> 이때 map이 아닌 forEach를 사용할 경우 빈값이 출력된다.

 

8-3. filter

list 안에 있는 요소 중 조건에 알맞는 것만 남겨서 새로운 저장소에 리스트 형태로 저장해라!

const newList = list.filter((elem) => {
     return elem < 3
})
 
console.log(newList) // [1,2]
 
// 축약
const newList = list.filter(elem => elem<3)

 

8-4. reduce

list 안에 있는 요소들을 하나씩 없애가며 최종적으로 하나의 값으로 만들고자 할 때 사용

const newValue = list.reduce((elem, current) =>
     return elem + current
}, 0) // current의 초기값임
 
console.log(newValue); // 15

 

어디에? 언제? 사용되는가!!

const max = list.reduce((elem, current) => {
     return Math.max(elem, current), list[0]
}) 

=> 리스트 안에서 가장 큰 값 출력할 때, 이상형 월드컵에서 선택된 것에 가중치 주고 최종 하나 뽑을 때 등....

 

 

9. Event Delegation 이벤트 위임

Event Bubbling 이벤트 버블링을 방지하기 위해 부모태그에 이벤트를 위임해버리는 것

 

이벤트 버블링의 예시>>

<body>

<!-- 이벤트 버블링 보기 -->
<div id="first" onclick = "printColorEvent('red')">
     <div id="second" onclick = "printColorEvent('orange')">
          <div id="third" onclick = "printColorEvent('yellow')"></div>
     </div>
</div>

<script>
const printColorEvent = (color) => {
     console.log(color)
}
</script>

</body>

#first에는 배경색 red, #second에는 orange, #third에는 yellow를 주고 아래와 같은 모습을 만들었다.

그리고 각 부분을 클릭하면 매개변수로 넘겨준 색깔에 출력되길 의도하였다. 

그러나 빨간 부분을 클릭했을 때는 red가 정상적으로 출력되지만

주황 부분을 클릭했을 때엔 // orange // red 가 동시에 출력되었고

노란 부분을 클릭했을 때엔 // yellow // orange // red 가 전부 동시에 출력되었다. 

 

<= 이것이 바로 자식에게 부여한 이벤트가 부모에게까지 전달되는 이벤트 버블링 때문이다.

 

<body>
<!-- 이벤트 버블링 해결하기 -->
<div id="first" onclick = "printColorEvent(event)">
     <div id="second">
          <div id="third"></div>
     </div>
</div>

<script>
 
const printColorEvent = (e) =>
     const targetId = e.target.id
     switch (targetId) {
         case "first":
             console.log("red")
             break
         case "second":
              console.log("orange")
              break
         case "third":
              console.log("yellow")
             break
     }
}
</script>
 
</body>

 

=> 함수를 자식들 하나하나에 일일히 넣어주는 게 아니고 부모에 한 번에 위임해버려!

 

 

<body>
<div id="parent" onclick="printValue(event)">
     <input type="button" value="경은">
     <input type="button" value="준연">
</div>
 
<input type="button" value="추가하기" onclick="add()">

<script>
const printValue = (e) => {
     console.log(e.target.value)
}

const add = () => {
     const tmpInput = document.createElement("input");
     tmpInput.value = "민석"
     tmpInput.type = "button"
     document.getElementById("parent").appendChild(tmpInput)
}
 
</script>
</body>

=> printValue라는 함수를 input 버튼 각각에 넣어준 것이 아니라,

그 버튼들을 감싸고 있는 부모 div 태그에 위임해버림으로써 이벤트 버블링을 막았다.