diff --git "a/Part2-1\355\214\200 /4\354\243\274\354\260\250-1/\354\240\204\354\204\261\355\230\204.md" "b/Part2-1\355\214\200 /4\354\243\274\354\260\250-1/\354\240\204\354\204\261\355\230\204.md" new file mode 100644 index 0000000..4d45e2b --- /dev/null +++ "b/Part2-1\355\214\200 /4\354\243\274\354\260\250-1/\354\240\204\354\204\261\355\230\204.md" @@ -0,0 +1,554 @@ +# 구조 분해 할당(destructing assignment) + +### 언제 사용하면 좋을까? + +- 객체나 배열에 저장된 데이터 전체가 아닌 일부만 필요한 경우 +- 함수의 매개변수가 많은 경우 +- 매개변수 기본값이 필요한 경우 + +## 배열 분해하기 + +구조 분해 할당을 활용하여 배열을 변수로 분해 + +```jsx +// 이름과 성을 요소로 가진 배열 +let arr = ["Seonghyeon", "Jun"] + +// firstName엔 arr[0]을 surname엔 arr[1]을 할당 +let [firstName, surname] = arr; + +alert(firstName); // Seonghyeon +alert(surname); // Jun +``` + +`split` 처럼 반환 값이 배열인 메서드를 활용 + +```jsx +let [firstName, surname] = "Seonghyeon Jun".split(' '); +``` + +쉼표를 사용하여 필요하지 않은 배열 요소를 버릴 수 있음 + +```jsx +// 두 번째 요소는 필요하지 않음 +let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; + +alert( title ); // Consul +``` + +할당 연산자 우측엔 배열 뿐만 아니라 모든 이터러블(반복 가능한 객체)이 올 수 있음 + +```jsx +let [a, b, c] = "abc"; // ["a", "b", "c"] +let [one, two, three] = new Set([1, 2, 3]); +``` + +할당 연산자 좌측엔 할당할 수 있는 것이면 어떤 것이든 올 수 있음 + +```jsx +let user = {}; +[user.name, user.surname] = "Seonghyeon Jun".split(' '); + +alert(user.name); // Seonghyeon +``` + +`.entries()`를 이용하면 키와 값을 순회해 변수로 분해 할당할 수 있음 + +```jsx + let user = { + name: "Choi", + age: 30 +}; + +// 객체의 키와 값 순회하기 +for (let [key, value] of Object.entries(user)) { + alert(`${key}:${value}`); // name:Choi, age:30이 차례대로 출력 +} +``` + +맵에도 활용이 가능 + +```jsx +let user = new Map(); +user.set("name", "Choi"); +user.set("age", "30"); + +for (let [key, value] of user) { + alert(`${key}:${value}`); // name:Choi, then age:30 +} +``` + +두 변수에 저장된 값을 교환할 때, 구조 분해 할당을 사용할 수 있음 + +```jsx +let nation = "Seoul"; +let city = "Korea"; + +// 변수 nation엔 Korea, 변수 city엔 Seoul이 저장되도록 값을 교환함 +[nation, city] = [city, nation]; // 임시 배열 생성 후 요소 순서를 교체하여 분해 + +alert(`${city}: ${Seoul}`); // city: Seoul` +``` + +❓ 이 방식을 사용하면 두 개뿐만 아니라 그 이상의 변수에 담긴 값을 교환할 수 있음 + +### **‘`...`’로 나머지 요소 가져오기** + +배열 앞쪽에 위치한 몇 개의 값만 필요하고 뒤에 있는 나머지 값들은 한 곳에 저장하고 싶다면? `...` 를 붙인 매개변수 하나를 추가하여 나머지(`rest`) 요소를 가져옴 + +```jsx +let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; + +alert(name1); // Julius +alert(name2); // Caesar + +alert(rest[0]); // Consul +alert(rest[1]); // of the Roman Republic +alert(rest.length); // 2 +``` + +`rest`는 나머지 배열 요소들이 저장된 새로운 배열이고, `rest` 대신 다른 이름 사용이 가능 + +변수 앞에 점 세 개(`…`)를 쓰고, 가장 마지막에 변수가 위치해야 함 + +### 기본값 + +할당하고자 하는 변수가 분해할 배열의 길이보다 크더라도 에러는 발생하지 않고 `undefined` 처리됨 + +```jsx +let [firstName, surname] = []; + +alert(firstName); // undefined +alert(surname); // undefined +``` + +`=` 를 이용하면 할당할 값이 없을 때 기본으로 할당할 기본값을 설정할 수 있음 + +```jsx +// 기본값 설정 +let [name = "Guest", surname = "Anonymous"] = ["Suzy"]; + +alert(name); // Suzy (배열에서 받아온 값) +alert(surname); // Anonymous (기본값) +``` + +기본값은 복잡한 표현식이나 함수 호출도 가능 + +```jsx +// name의 prompt만 실행됨 +let [surname = prompt('성을 입력하세요.'), name = prompt('이름을 입력하세요.')] = ["김"]; + +alert(surname); // 김 (배열에서 받아온 값) +alert(name); // prompt에서 받아온 값 +``` + +## 객체 분해하기 + +기본 문법 + +```jsx +let {var1, var2} = {var1: …, var2: …} +``` + +할당 연산자 우측엔 분해할 객체를, 좌측엔 상응하는 객체 프로퍼티의 패턴을 넣음 + +프로퍼티 `options.title`, `options.width`, `options.height`에 저장된 값이 상응하는 변수에 할당되었고, 순서가 바뀌어도 동일하게 동작함 + +```jsx +let options = { + title: "Menu", + width: 100, + height: 200 +}; + +let {title, width, height} = options; + +alert(title); // Menu +alert(width); // 100 +alert(height); // 200 + +// let {...} 안의 순서가 바뀌어도 동일하게 동작함 +let {height, width, title} = { title: "Menu", height: 200, width: 100 } +``` + +객체 프로퍼티를 기존 프로퍼티 키와 다른 이름의 변수에 저장할 수 있고, 이 때 콜론(`:`)을 사용함 + +```jsx +// { 객체 프로퍼티: 목표 변수 } +let {width: w, height: h, title} = options; + +// width -> w, height -> h +alert(title); // Menu +alert(w); // 100 +alert(h); // 200 +``` + +프로퍼티가 없는 경우를 대비하여 `=`를 사용해 기본값 설정이 가능 + +```jsx +let options = { + title: "Menu" +}; + +let {width = 100, height = 200, title} = options; // 기본값 설정 + +alert(title); // Menu +alert(width); // 100 +alert(height); // 200 +``` + +콜론과 할당 연산자를 동시에 사용하는 것도 가능 + +```jsx +let {width: w = 100, height: h = 200, title} = options; +``` + +프로퍼티가 많은 복잡한 객체에서 원하는 정보만 가져오기 + +```jsx +let options = { + title: "Menu", + width: 100, + height: 200 +}; + +// title만 변수로 가져오기 +let { title } = options; + +alert(title); // Menu +``` + +### **나머지 패턴 ‘`...`'** + +배열에서 했던 것처럼 나머지 패턴을 사용하여 남은 프로퍼티를 할당할 수 있음 (구식 브라우저는 나머지 패턴을 지원하지 않아서 바벨(Babel)을 이용해야 함) + +```jsx +let options = { + title: "Menu", + height: 200, + width: 100 +}; + +// title = 이름이 title인 프로퍼티 +// rest = 나머지 프로퍼티들 +let {title, ...rest} = options; + +// title엔 "Menu", rest엔 {height: 200, width: 100}이 할당됨 +alert(rest.height); // 200 +alert(rest.width); // 100 +``` + +**`let`없이 사용하기** + +`let`으로 새로운 변수를 선언하지 않고, 기존에 있는 변수에 분해한 값을 할당할 수 있음 + +```jsx +let title, width, height; + +// SyntaxError: Unexpected token '=' 이라는 에러가 아랫줄에서 발생 +{title, width, height} = {title: "Menu", width: 200, height: 100}; +``` + +자바스크립트는 표현식 안에 없는 중괄호`{...}`를 코드 블록으로 인식하는데, 이 때 소괄호 `(...)`로 감싸면 표현식으로 인식하여 에러를 해결할 수 있음 + +```jsx +({title, width, height} = {title: "Menu", width: 200, height: 100}); + +alert( title ); // Menu +``` + +## 중첩 구조 분해(nested destructing) + +객체나 배열이 다른 객체나 배열을 포함하는 경우, 패턴을 사용하여 중첩 배열이나 객체의 정보를 추출하는 것 + +```jsx +let options = { + size: { + width: 100, + height: 200 + }, + items: ["Cake", "Donut"], + extra: true +}; + +// 코드를 여러 줄에 걸쳐 작성해 의도하는 바를 명확히 드러냄 +let { + size: { // size는 여기에, + width, + height + }, + items: [item1, item2], // items는 여기에 할당함 + title = "Menu" // 분해할 객체에 title 프로퍼티가 없으므로 기본값을 사용함 +} = options; + +alert(title); // Menu +alert(width); // 100 +alert(height); // 200 +alert(item1); // Cake +alert(item2); // Donut +``` + +할당 연산자 좌측 패턴에 없는 `extra`를 제외한 `options`의 모든 프로퍼티가 상응하는 변수에 할당됨 + +## 똑똑한 함수 매개변수 + +함수에 많은 매개변수 중 일부 정보만 받는 메뉴 생성 함수 `showMenu`의 예시 + +```jsx +function showMenu(title = "Untitled", width = 200, height = 100, items = []) { + ... +} +``` + +넘겨줄 인수의 순서가 틀려 문제가 발생 가능성이 있음 + +매개변수에 기본값이 설정되어 있어 인수를 넘겨주지 않아도 문제가 발생 + +```jsx +// 기본값을 사용할 경우에도 아래와 같이 undefined를 넘겨줘야 함 +showMenu("My Menu", undefined, undefined, ["Item1", "Item2"]) +``` + +모든 매개변수를 객체에 모아 전달하고 함수는 전달 받은 객체를 변수에 할당함 + +```jsx +// 함수에 전달할 객체 +let options = { + title: "My menu", + items: ["Item1", "Item2"] +}; + +// 똑똑한 함수는 전달받은 객체를 분해해 변수에 할당함 +function showMenu({title = "Untitled", width = 200, height = 100, items = []}) { + // title, items – 객체 options에서 가져옴 + // width, height – 기본값 + alert( `${title} ${width} ${height}` ); // My Menu 200 100 + alert( items ); // Item1, Item2 +} + +showMenu(options); +``` + +중첩 객체와 콜론을 사용하여 좀 더 복잡한 구조 분해도 가능함 + +```jsx +function showMenu({ + title = "Untitled", + width: w = 100, // width는 w에, + height: h = 200, // height는 h에, + items: [item1, item2] // items의 첫 번째 요소는 item1에, 두 번째 요소는 item2에 할당 +}) { + alert( `${title} ${w} ${h}` ); // My Menu 100 200 + alert( item1 ); // Item1 + alert( item2 ); // Item2 +} + +showMenu(options); +``` + +⚠️ 함수 매개변수를 구조 분해할 땐, 반드시 인수가 전달된다고 가정하고 사용 + +기본값을 할당하려면 빈 객체를 명시적으로 전달하기 + +```jsx +showMenu({}); // 모든 인수에 기본값이 할당됨 +showMenu(); // 에러가 발생할 수 있음 +``` + +인수 객체의 기본값을 빈 객체 `{}`로 설정하면 함수에 인수를 전달하지 않아도 에러가 발생하지 않음 + +# 나머지 매개변수와 전개 구문 + +## 나머지 매개변수 `...` + +함수의 정의 방법과 상관없이 함수에 넘겨주는 인수 개수에는 제약이 없음 + +인수 두 개를 받도록 함수를 정의하고, 함수를 호출할 때 두 개보다 많은 인수를 전달 + +```jsx +function sum(a, b) { + return a + b; +} + +alert( sum(1, 2, 3, 4, 5) ); +``` + +에러는 발생하지 않지만, 반환값은 처음 두 개의 인수만 사용하여 계산됨 + +여분의 매개변수는 그 값들을 담을 배열 이름 앞에 `...`을 붙여 함수를 선언부에 포함시킬 수 있음 + +```jsx +function sumAll(...args) { // args는 배열 이름, 모든 인수가 모임 + let sum = 0; + + for (let arg of args) sum += arg; + + return sum; +} + +alert( sumAll(1) ); // 1 +alert( sumAll(1, 2) ); // 3 +alert( sumAll(1, 2, 3) ); // 6 +``` + +앞부분의 매개변수는 변수로, 남아 있는 매개변수들은 배열로 모을 수 있음 +아래 코드처럼 처음 두 인수는 변수에, 나머지 인수들은 `titles`라는 배열에 할당 + +```jsx +function showName(firstName, lastName, ...titles) { + alert( firstName + ' ' + lastName ); // Bora Lee + + // 나머지 인수들은 배열 titles의 요소 + // titles = ["Software Engineer", "Researcher"] + alert( titles[0] ); // Software Engineer + alert( titles[1] ); // Researcher + alert( titles.length ); // 2 +} + +showName("Bora", "Lee", "Software Engineer", "Researcher"); +``` + +⚠️ 나머지 매개변수는 항상 마지막에 있어야 에러가 발생하지 않음 + +## arguments 객체 + +유사 배열 객체인 `arguments`를 사용하면 인덱스를 사용해 인수에 접근할 수 있음 + +```jsx +function showName() { + alert( arguments.length ); + alert( arguments[0] ); + alert( arguments[1] ); + + // arguments는 이터러블 객체이기 때문에 + // for(let arg of arguments) alert(arg); 를 사용해 인수를 펼칠 수 있음 +} + +// 2, Bora, Lee가 출력됨 +showName("Bora", "Lee"); + +// 1, Bora, undefined가 출력됨(두 번째 인수는 없음) +showName("Bora"); +``` + +`arguments`는 유사 배열 객체이면서 이터러블(반복 가능한) 객체이지만 배열은 아니기 때문에 배열 메소드는 사용할 수 없음 (`arguments.map (…)` 호출 불가) + +`arguments`는 인수 전체를 담기 때문에 나머지 매개변수처럼 인수의 일부만 사용할 수 없음 + +배열 메소드를 사용하거나 인수 일부만 사용할 때는 나머지 매개변수를 사용하기 + +화살표 함수는 `arguments` 객체를 지원하지 않음 + +화살표 함수에서 `arguments` 객체에 접근하면 외부에 있는 일반 함수의 arguments 객체를 가져옴 + +```jsx +function f() { + let showArg = () => alert(arguments[0]); + showArg(); +} + +f(1); // 1 +``` + +## 스프레드 문법 + +매개변수 목록을 배열로 가져오는 방법과 반대로 배열을 통째로 매개변수에 넘겨주고 싶다면? + +배열 `[3, 5, 1]`을 `Math.max`로 호출할 경우 (내장 함수 `Math.max`는 인수로 받은 숫자 중 가장 큰 숫자를 반환) + +```jsx +alert( Math.max(3, 5, 1) ); // 5 + +// Math.max 함수는 숫자를 인수로 받기 때문에 동작하지 않음 +let arr = [3, 5, 1]; + +alert( Math.max(arr) ); // NaN +``` + +스프레드 문법 `...`을 사용하면 이터러블 객체 `arr`이 인수 목록으로 확장되어 5를 반환함 + +```jsx +alert( Math.max(...arr) ); // 5 (스프레드 구문이 배열을 인수 목록으로 바꿔줌) +``` + +여러 개의 객체를 전달할 수도 있고, 배열을 합칠 때도 활용할 수 있음 + +```jsx +// 여러 개의 객체 전달하기 +let arr1 = [1, -2, 3, 4]; +let arr2 = [8, 3, -8, 1]; + +alert( Math.max(...arr1, ...arr2) ); // 8 + +// 두 개의 배열을 하나로 합치기 +let arr = [3, 5, 1]; +let arr2 = [8, 9, 15]; + +let merged = [0, ...arr, 2, ...arr2]; + +alert(merged); // 0,3,5,1,2,8,9,15 (0, arr, 2, arr2 순서로 합쳐짐) +``` + +배열이 아니더라도 이터러블 객체라면 스프레드 문법을 사용할 수 있음 + +스프레드 문법을 사용해 문자열을 문자 배열로 바꾸기 + +```jsx +let str = "Hello"; + +alert( [...str] ); // H,e,l,l,o +``` + +스프레드 문법은 `for...of`와 같은 방식으로 내부에서 이터레이터를 사용해 요소를 수집함 + +메소드 `Array.from`을 사용해도 이터러블 객체인 문자열을 배열로 바꿔줌 + +**`Array.from`과 `[...obj]`의 차이점** + +`Array.from`은 유사 배열 객체와 이터러블 객체 모두 사용이 가능 + +스프레드 문법 `...`은 이터러블 객체에만 사용이 가능 + +➡️ 무언가를 배열로 바꿀 때는 스프레드 문법보다 `Array.from`이 보편적으로 사용됨 + +## 배열과 객체의 복사본 만들기 + +스프레드 문법을 사용하여 배열 복사하기 + +```jsx +let arr = [1, 2, 3]; +let arrCopy = [...arr]; // 배열을 펼쳐서 각 요소를 분리 후 + // 매개변수 목록으로 만들어 새로운 배열에 할당함 + +// 배열 복사본의 요소가 기존 배열 요소와 같을까? +alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true + +// 두 배열은 같을까? +alert(arr === arrCopy); // false (참조가 다름) + +// 참조가 다르므로 기존 배열을 수정해도 복사본은 영향을 받지 않음 +arr.push(4); +alert(arr); // 1, 2, 3, 4 +alert(arrCopy); // 1, 2, 3 +``` + +스프레드 문법을 사용하여 객체 복사하기 + +```jsx +let obj = { a: 1, b: 2, c: 3 }; +let objCopy = { ...obj }; // 객체를 펼쳐서 각 요소를 분리 후 + // 매개변수 목록을 만들어 새로운 객체에 할당함 + +// 객체 복사본의 프로퍼티들이 기존 객체의 프로퍼티들과 같을까? +alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true + +// 두 객체는 같을까? +alert(obj === objCopy); // false (참조가 다름) + +// 참조가 다르므로 기존 객체를 수정해도 복사본은 영향을 받지 않음 +obj.d = 4; +alert(JSON.stringify(obj)); // {"a":1,"b":2,"c":3,"d":4} +alert(JSON.stringify(objCopy)); // {"a":1,"b":2,"c":3} +``` + + 스프레드 문법은 `Object.assign()`을 사용하여 배열이나 객체를 복사하는 것보다 코드가 짧아서 선호됨