-
유용한 JS 문법(2) - 모던 자바스크립트 | 김민준Javascript/ECMAScript 2015 (ES6) 2020. 6. 24. 23:43반응형
[출처 : https://learnjs.vlpt.us]
spread
일단 spread 문법부터 알아봅시다. spread 라는 단어가 가지고 있는 의미는 펼치다, 퍼뜨리다 입니다. 이 문법을 사용하면, 객체 혹은 배열을 펼칠수있습니다.
예를 들어서 다음과 같은 객체들이 있다고 가정해봅시다.
const slime = { name: '슬라임' }; const cuteSlime = { name: '슬라임', attribute: '귀여운' }; const purpleCuteSlime = { name: '슬라임', attribute: '귀여운', color: '보라' }; console.log(slime); // {name: "슬라임"} console.log(cuteSlime); // {name: "슬라임", attribute: "귀여운"} console.log(purpleCuteSlime); // {name: "슬라임", attribute: "귀여운", color: "보라"}
이 코드에서는 먼저 slime 이라는 객체를 선언했습니다. 그리고 cuteSlime 이라는 객체를 만들었는데요, 기존에 선언한 slime 을 건들이지 않고 새로운 객체를 만들어서 slime 이 가지고 있는 값을 그대로 사용하였습니다.
그 다음에는 purpleCuteSlime 이라는 객체를 만들었는데요, 이 객체는 cuteSlime 이 가지고 있는 속성을 그대로 사용하면서 추가적으로 color 가 추가되었습니다.
위 코드에서의 핵심은, 기존의 것을 건들이지 않고, 새로운 객체를 만든다는 것 인데요, 이러한 상황에 사용 할 수 있는 유용한 문법이 spread 입니다.
아까 코드는 spread 문법을 사용하면 다음과 같이 작성 할 수 있습니다.
const slime = { name: '슬라임' }; const cuteSlime = { ...slime, attribute: '귀여운' }; const purpleCuteSlime = { ...cuteSlime, color: '보라' }; console.log(slime); // {name: "슬라임"} console.log(cuteSlime); // {name: "슬라임", attribute: "귀여운"} console.log(purpleCuteSlime); // {name: "슬라임", attribute: "귀여운", color: "보라"}
여기서 사용한 ... 문자가 바로 spread 연산자입니다.
spread 연산자는 배열에서도 사용 할 수 있습니다.
const animals = ['개', '고양이', '참새']; const anotherAnimals = [...animals, '비둘기']; console.log(animals); console.log(anotherAnimals);
기존의 animals 는 건드리지 않으면서, 새로운 anotherAnimals 배열에 animals 가 가지고 있는 내용을 모두 집어넣고, '비둘기' 라는 항목을 추가적으로 넣었습니다.
배열에서 spread 연산자를 여러번 사용 할 수도 있습니다.
const numbers = [1, 2, 3, 4, 5]; const spreadNumbers = [...numbers, 1000, ...numbers]; console.log(spreadNumbers); // [1, 2, 3, 4, 5, 1000, 1, 2, 3, 4, 5]
함수 인자와 spread
이번에는, 다시 아까 배웠던 spread 로 돌아와서 한가지를 더 가르쳐드리겠습니다. 바로 함수의 인자와 spread 인데요, 만약 프로그래밍을 처음 배우신다면 파라미터와 인자가 좀 헷갈릴 수 있습니다.
이에 대해서 간단하게 설명 드려볼게요.
const myFunction(a) { // 여기서 a 는 파라미터 console.log(a); // 여기서 a 는 인자 } myFunction('hello world'); // 여기서 'hello world' 는 인자
함수에서 값을 읽을때, 그 값들은 파라미터라고 부릅니다. 그리고 함수에서 값을 넣어줄 때, 그 값들은 인자라고 부릅니다.
인자가 무엇인지 이해를 하셨다면 이제 함수인자와 spread 문법을 사용하는 것에 대하여 알아볼게요.
우리가 방금 함수파라미터와 rest 를 사용한 것과 비슷한데, 반대의 역할입니다. 예를 들어서, 우리가 배열 안에 있는 원소들을 모두 파라미터로 넣어주고 싶다고 가정해보겠습니다.
function sum(...rest) { return rest.reduce((acc, current) => acc + current, 0); } const numbers = [1, 2, 3, 4, 5, 6]; const result = sum(...numbers); console.log(result);
rest
rest는 생김새는 spread 랑 비슷한데, 역할이 매우 다릅니다.
rest는 객체, 배열, 그리고 함수의 파라미터에서 사용이 가능합니다.
객체에서의 rest
우선 객체에서의 예시를 알아볼까요?
const purpleCuteSlime = { name: '슬라임', attribute: '귀여운', color: '보라' }; const { color, ...rest } = purpleCuteSlime; console.log(color); // "보라" console.log(rest); // {name: "슬라임", attribute: "귀여운"}
rest 안에 color 값을 제외한 값이 들어있습니다.
rest 는 객체와 배열에서 사용 할 때는 이렇게 비구조화 할당 문법과 함께 사용됩니다. 주로 사용 할때는 위와 같이 rest 라는 키워드를 사용하게 되는데요, 추출한 값의 이름이 꼭 rest 일 필요는 없습니다.
배열에서의 rest
다음, 배열에서의 사용 예시를 알아봅시다.
const numbers = [0, 1, 2, 3, 4, 5, 6]; const [one, ...rest] = numbers; console.log(one); // 0 console.log(rest); // [1, 2, 3, 4, 5, 6]
배열 비구조화 할당을 통하여 원하는 값을 밖으로 꺼내고, 나머지 값을 rest 안에 넣었습니다.
반면 rest 문법을 먼저 선언하는 건 안됩니다.
const numbers = [0, 1, 2, 3, 4, 5, 6]; const [..rest, last] = numbers;
함수 파라미터에서의 rest
rest 를 함수파라미터에서 사용 할 수도 있습니다.
예를 들어서 우리가 파라미터로 넣어준 모든 값들을 합해주는 함수를 만들어주고 싶다고 가정해봅시다.
function sum(a, b, c, d, e, f, g) { return a + b + c + d + e + f + g; } const result = sum(1, 2, 3, 4, 5, 6); console.log(result) // NaN
위에서의 sum 함수는 7개의 파라미터를 받아오는데, 아래에서 사용 할때에는 6개만 넣어줬습니다. 그러면, g 값이 undefined 가 되기 때문에 sum 에 더하는 과정에서 += undefined 를 하게 되면 결과는 NaN 이 되버립니다. 그렇기 때문에 함수에서 하나하나 유효한 값인지 확인을 해줬지요.
함수의 파라미터가 몇개가 될 지 모르는 상황에서 rest 파라미터를 사용하면 매우 유용합니다.
코드를 다음과 같이 수정해보세요.
function sum(...rest) { return rest; } const result = sum(1, 2, 3, 4, 5, 6); console.log(result); // [1, 2, 3, 4, 5, 6]
result 가 가르키고 있는 것은 함수에서 받아온 파라미터들로 이루어진 배열입니다.
우리가 이제 파라미터들이 들어가있는 배열을 받았으니, 그냥 모두 더해주면 되겠죠?
function sum(...rest) { return rest.reduce((acc, current) => acc + current, 0); } const result = sum(1, 2, 3, 4, 5, 6); console.log(result); // 21
Scope
Scope 란, 우리가 변수 혹은 함수를 선언하게 될 때 해당 변수 또는 함수가 유효한 범위를 의미합니다. Scope 는 총 3가지 종류가있습니다.
· Global (전역) Scope : 코드의 모든 범위에서 사용이 가능합니다.
· Function (함수) Scope : 함수 안에서만 사용이 가능합니다.
· lock (블록) Scope : if, for, switch 등 특정 블록(중괄호 : {}) 내부에서만 사용이 가능합니다.
예시를 통한 Scope 이해
const value = 'hello!'; function myFunction() { // function scope console.log(value); // "hello!" } function otherFunction() { // function scope const value = 'bye!'; console.log(value); // "bye!" } myFunction(); otherFunction(); // global scope console.log(value); // "hello!"
코드의 맨 윗줄에서 선언된 value 값은 Global Scope 로 선언된 값입니다. Global Scope 로 선언된 값은 어디서든지 사용이 가능합니다. myFunction 에서 바로 사용을 할 수 있었지요?
otherFunction 에서는 함수 내부에서 value 값을 'bye!' 로 새로 선언을 해주었습니다. 이렇게 되면, value 라는 값은 Function Scope 로 지정이 되서 해당 값은 otherFunction 내부에서만 유효한 값이 됩니다. 이렇게 값을 설정한다고 해서 기존에 Global Scope 로 선언된 value 값이 바뀌지 않습니다.
또 다른 예시를 확인해봅시다.
const value = 'hello!'; function myFunction() { // function scope const value = 'bye!'; const anotherValue = 'world'; function functionInside() { // function scope console.log(value); // "bye!" console.log(anotherValue); // "world" } functionInside(); } myFunction() // global scope console.log(value); // "hello!" console.log(anotherValue); // ReferenceError: anotherValue is not defined
myFunction 내부에 anotherValue 라는 새로운 값을 선언했고, functionInside 라는 함수도 선언을 했습니다. functionInside 함수에서는 myFunction 에서 선언한 value 값과 anotherValue 값을 조회 할 수 있습니다.
반면, myFunction 밖에서는 anotherValue 를 조회 할 수 없습니다.
이제, 또 다른 예시를 알아봅시다.
const value = 'hello!'; function myFunction() { const value = 'bye!'; if (true) { // block scope const value = 'world'; console.log(value); // "world" } // function scope console.log(value); // "bye!" } myFunction(); // global scope console.log(value); // "hello!"
const 로 선언한 값은 Block Scope 로 선언이 됩니다. 따라서, if 문 같은 블록 내에서 새로운 변수/상수를 선언하게 된다면, 해당 블록 내부에서만 사용이 가능하며, 블록 밖의 범위에서 똑같은 이름을 가진 값이 있다고 해도 영향을 주지 않습니다. let 또한 마찬가지 입니다.
하지만 var 는 어떨까요?
var value = 'hello!'; function myFunction() { // function scope var value = 'bye!'; console.log(value); // "bye!" if (true) { // block scope var value = 'world'; console.log(value); // "world" } // function scope console.log(value); // "world" } myFunction(); // global scope console.log(value); // "hello!"
var 는 Function Scope 로 선언이 되므로, if 문 블록 내부에서 선언한 value 값이 블록 밖의 value 에도 영향을 미치게 됩니다.
Hoisting 이해하기
Hoisting 이란, 자바스크립트에서 아직 선언되지 않은 함수/변수를 "끌어올려서" 사용 할 수 있는 자바스크립트의 작동 방식을 의미합니다.
다음 코드를 확인해보세요.
myFunction(); // "hello world!" function myFunction() { console.log('hello world!'); }
위 코드에서는 myFunction 함수를 선언하기 전에, myFunction() 을 호출해주었습니다. 함수가 아직 선언되지 않았음에도 불구하고 코드는 정상적으로 작동하게 됩니다.
이게 잘 작동하는 이유는, 자바스크립트 엔진이 위 코드를 해석하는 과정에서, 다음과 같이 받아들이게 되기 때문입니다.
function myFunction() { console.log('hello world!'); } myFunction(); // "hello world!"
이러한 현상을, Hoisting 이라고 부릅니다.
변수 또한 Hoisting 됩니다.
예를 들어서, 다음과 같은 코드를 실행한다면,
console.log(number); // ReferenceError: number is not defined
이런 오류가 발생합니다.
그렇지만, 다음과 같은 코드는
console.log(number); // undefined var number = 2;
undefined 가 출력됩니다.
자바스크립트 엔진이 위 코드를 해석 할 때는 다음과 같이 받아들이게 됩니다.
var number; console.log(number); // undefined number = 2;
반면, const 와 let 은 hosting은 되지만 변수 생성 과정이 달라 일시적인 사각지대(TDZ)가 생성되어 초기화 전에는 액세스할 수 없다는 ReferenceError를 표시합니다.
function fn() { console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization let a = 2; } fn();
Hoisting 은 자바스크립트 엔진이 갖고 있는 성질이며, Hoisting 을 일부러 할 필요는 없지만, 방지하는 것이 좋습니다. 왜냐하면 Hoisting 이 발생하는 코드는 이해하기 어렵기 때문에 유지보수도 힘들어지고 의도치 않는 결과물이 나타나기 쉽기 때문입니다.
Hoisting 을 방지하기 위해서, 함수의 경우 꼭 선언 후에 호출을 하도록 주의를 하시고, var 대신 const, let 을 위주로 사용하세요. 추가적으로, 나중에 자바스크립트 개발을 본격적으로 하게 될 때에는 ESLint 라는것을 사용하여 Hoisting 이 발생하는 코드는 에디터상에서 쉽게 발견해낼 수 있습니다.
반응형'Javascript > ECMAScript 2015 (ES6)' 카테고리의 다른 글
Axios (0) 2022.09.28 비동기 처리 - 모던 자바스크립트 | 김민준 (0) 2020.06.25 JavaScript 입문(1) - 모던 자바스크립트 | 김민준 (0) 2020.06.18 제너레이터와 async/awit - 문법 | Poiemaweb (0) 2020.03.26 이터레이션과 for...of 문 - 문법 | Poiemaweb (0) 2020.03.18