JavaScript에서 가장 많이 사용되는 두 가지 데이터 구조는 Object
와 Array
입니다.
객체를 사용하면 키별로 데이터 항목을 저장하는 단일 엔터티를 만들 수 있습니다.
배열을 사용하면 데이터 항목을 정렬된 목록으로 수집할 수 있습니다.
그러나 이를 함수에 전달하면 전부 필요하지 않을 수도 있습니다. 함수에는 특정 요소나 속성만 필요할 수 있습니다.
구조 분해 할당 은 배열이나 객체를 여러 변수로 "압축해제"할 수 있는 특수 구문입니다. 때로는 그게 더 편리하기 때문입니다.
구조 분해는 매개변수, 기본값 등이 많은 복잡한 함수에서도 잘 작동합니다. 곧 우리는 그것을 보게 될 것입니다.
다음은 배열이 변수로 구조 해제되는 방법에 대한 예입니다.
// 이름과 성이 있는 배열이 있습니다. let arr = ["John", "Smith"] // 구조 분해 할당 // firstName = arr[0]을 설정합니다. // 그리고 성 = arr[1] let [이름, 성] = arr; 경고(이름); // 존 경고(성); // 스미스
이제 배열 멤버 대신 변수를 사용하여 작업할 수 있습니다.
split
또는 기타 배열 반환 방법과 결합하면 멋지게 보입니다.
let [firstName, surname] = "John Smith".split(' '); 경고(이름); // 존 경고(성); // 스미스
보시다시피 구문은 간단합니다. 하지만 몇 가지 특이한 세부 사항이 있습니다. 더 잘 이해하기 위해 더 많은 예를 살펴보겠습니다.
'구조화'는 '파괴적'을 의미하지 않습니다.
항목을 변수에 복사하여 "구조화 해제"하므로 "구조화 해제 할당"이라고 합니다. 그러나 배열 자체는 수정되지 않습니다.
간단히 작성하는 방법은 다음과 같습니다.
// [이름, 성] = arr; firstName = arr[0]; 성 = arr[1];
쉼표를 사용하여 요소 무시
배열의 원하지 않는 요소는 추가 쉼표를 통해 제거될 수도 있습니다.
// 두 번째 요소는 필요하지 않습니다. let [firstName, , title] = ["율리우스", "카이사르", "영사", "로마 공화국의"]; 경고( 제목 ); // 영사
위 코드에서는 배열의 두 번째 요소를 건너뛰고, 세 번째 요소를 title
에 할당하고, 나머지 배열 항목도 건너뜁니다(변수가 없으므로).
오른쪽의 모든 반복 가능 항목과 함께 작동합니다.
… 실제로 배열뿐만 아니라 모든 반복 가능 항목과 함께 사용할 수 있습니다.
[a, b, c] = "abc"라고 하자; // ["a", "b", "c"] let [하나, 둘, 셋] = new Set([1, 2, 3]);
내부적으로 구조 분해 할당은 올바른 값을 반복하여 작동하기 때문에 작동합니다. =
오른쪽에 있는 값에 대해 for..of
호출하고 값을 할당하기 위한 일종의 구문 설탕입니다.
왼쪽에 있는 항목에 할당
왼쪽에 있는 "할당 가능 항목"을 사용할 수 있습니다.
예를 들어 객체 속성은 다음과 같습니다.
사용자 = {}로 두십시오; [user.name, user.surname] = "John Smith".split(' '); 경고(사용자.이름); // 존 경고(사용자.성); // 스미스
.entries()를 사용한 반복
이전 장에서 우리는 Object.entries(obj) 메소드를 보았습니다.
객체의 키와 값을 반복하기 위해 구조 분해와 함께 사용할 수 있습니다.
사용자 = { 이름: "존", 나이: 30 }; // 키와 값을 반복합니다. for (Object.entries(user)의 [키, 값]하자) { 경고(`${키}:${값}`); // 이름:John, 나이:30 }
Map
에 대한 유사한 코드는 반복 가능하므로 더 간단합니다.
사용자 = 새로운 Map()을 보자; user.set("이름", "John"); user.set("나이", "30"); // 맵은 [키, 값] 쌍으로 반복되므로 구조 분해에 매우 편리합니다. for (사용자의 [키, 값]하자) { 경고(`${키}:${값}`); // 이름:John, 나이:30 }
변수 교환 트릭
구조 분해 할당을 사용하여 두 변수의 값을 바꾸는 잘 알려진 트릭이 있습니다.
손님 = "제인"이라고 놔두세요; let admin = "피트"; // 값을 바꾸자: guest=Pete, admin=Jane으로 설정 [게스트, 관리자] = [관리자, 게스트]; Alert(`${guest} ${admin}`); // Pete Jane (성공적으로 교체되었습니다!)
여기서는 두 변수의 임시 배열을 생성하고 즉시 교환된 순서로 구조를 해제합니다.
이런 식으로 두 개 이상의 변수를 교환할 수 있습니다.
일반적으로 배열이 왼쪽 목록보다 길면 "추가" 항목은 생략됩니다.
예를 들어, 여기서는 두 항목만 선택되고 나머지는 무시됩니다.
let [name1, name2] = ["율리우스", "카이사르", "영사", "로마 공화국의"]; 경고(이름1); // 율리우스 경고(이름2); // 카이사르 // 추가 항목은 어디에도 할당되지 않습니다.
다음에 나오는 모든 항목도 수집하려면 점 세 개 "..."
사용하여 "나머지"를 가져오는 매개변수를 하나 더 추가할 수 있습니다.
let [name1, name2, ...rest] = ["율리우스", "카이사르", "영사", "로마 공화국의"]; // 나머지는 세 번째 항목부터 시작하는 항목의 배열입니다. 경고(휴식[0]); // 영사 경고(휴식[1]); // 로마 공화국의 경고(휴식.길이); // 2
rest
값은 나머지 배열 요소의 배열입니다.
rest
대신 다른 변수 이름을 사용할 수 있습니다. 변수 이름 앞에 점이 세 개 있고 구조 분해 할당에서 마지막에 있는지 확인하세요.
let [name1, name2, ...titles] = ["율리우스", "카이사르", "영사", "로마 공화국의"]; // 이제 titles = ["영사", "로마 공화국의"]
배열이 왼쪽의 변수 목록보다 짧으면 오류가 발생하지 않습니다. 존재하지 않는 값은 정의되지 않은 것으로 간주됩니다.
let [이름, 성] = []; 경고(이름); // 한정되지 않은 경고(성); // 한정되지 않은
누락된 값을 대체하기 위해 "기본값"을 원하는 경우 =
사용하여 제공할 수 있습니다.
// 기본값 let [이름 = "게스트", 성 = "익명"] = ["줄리어스"]; 경고(이름); // Julius (배열에서) 경고(성); // 익명(기본값 사용됨)
기본값은 더 복잡한 표현식일 수도 있고 함수 호출일 수도 있습니다. 값이 제공되지 않은 경우에만 평가됩니다.
예를 들어, 여기서는 두 가지 기본값에 대해 prompt
기능을 사용합니다.
//성에 대한 프롬프트만 실행합니다. let [이름 = 프롬프트('이름?'), 성 = 프롬프트('성?')] = ["줄리어스"]; 경고(이름); // Julius (배열에서) 경고(성); // 어떤 프롬프트가 나오든
참고: prompt
누락된 값( surname
)에 대해서만 실행됩니다.
구조 분해 할당은 객체에도 작동합니다.
기본 구문은 다음과 같습니다.
let {var1, var2} = {var1:…, var2:…}
변수로 분할하려는 기존 개체가 오른쪽에 있어야 합니다. 왼쪽에는 해당 속성에 대한 객체와 같은 "패턴"이 포함되어 있습니다. 가장 간단한 경우에는 {...}
의 변수 이름 목록입니다.
예를 들어:
옵션 = { 제목: "메뉴", 너비: 100, 높이: 200 }; {제목, 너비, 높이} = 옵션을 설정합니다. 경고(제목); // 메뉴 경고(너비); // 100 경고(높이); // 200
속성 options.title
, options.width
및 options.height
해당 변수에 할당됩니다.
순서는 중요하지 않습니다. 이것도 작동합니다:
// let {...}에서 순서를 변경했습니다. let {높이, 너비, 제목} = { 제목: "메뉴", 높이: 200, 너비: 100 }
왼쪽의 패턴은 더 복잡할 수 있으며 속성과 변수 간의 매핑을 지정합니다.
예를 들어, options.width
가 w
라는 변수에 들어가도록 하는 것과 같이 다른 이름을 가진 변수에 속성을 할당하려는 경우 콜론을 사용하여 변수 이름을 설정할 수 있습니다.
옵션 = { 제목: "메뉴", 너비: 100, 높이: 200 }; // { 소스속성: targetVariable } let {너비: w, 높이: h, 제목} = 옵션; // 너비 -> w // 높이 -> h // 제목 -> 제목 경고(제목); // 메뉴 경고(w); // 100 경고(h); // 200
콜론은 “무엇: 어디로 가는가”를 나타냅니다. 위의 예에서 속성 width
w
로, 속성 height
h
로, title
동일한 이름에 할당됩니다.
잠재적으로 누락된 속성의 경우 다음과 같이 "="
사용하여 기본값을 설정할 수 있습니다.
옵션 = { 제목: "메뉴" }; let {너비 = 100, 높이 = 200, 제목} = 옵션; 경고(제목); // 메뉴 경고(너비); // 100 경고(높이); // 200
배열이나 함수 매개변수와 마찬가지로 기본값은 모든 표현식이나 함수 호출일 수 있습니다. 값이 제공되지 않으면 평가됩니다.
아래 코드에서 prompt
width
를 묻지만 title
묻지 않습니다.
옵션 = { 제목: "메뉴" }; let {width = 프롬프트("너비?"), title = 프롬프트("제목?")} = 옵션; 경고(제목); // 메뉴 경고(너비); // (프롬프트의 결과가 무엇이든)
콜론과 같음을 결합할 수도 있습니다.
옵션 = { 제목: "메뉴" }; let {너비: w = 100, 높이: h = 200, 제목} = 옵션; 경고(제목); // 메뉴 경고(w); // 100 경고(h); // 200
많은 속성을 가진 복잡한 객체가 있는 경우 필요한 것만 추출할 수 있습니다.
옵션 = { 제목: "메뉴", 너비: 100, 높이: 200 }; // 제목만 변수로 추출 { 제목 } = 옵션을 보자; 경고(제목); // 메뉴
객체에 변수보다 더 많은 속성이 있으면 어떻게 되나요? 일부를 취하고 "나머지"를 어딘가에 할당할 수 있습니까?
배열에서 했던 것처럼 나머지 패턴을 사용할 수 있습니다. 일부 오래된 브라우저(IE, Babel을 사용하여 폴리필)에서는 지원되지 않지만 최신 브라우저에서는 작동합니다.
다음과 같습니다:
옵션 = { 제목: "메뉴", 높이: 200, 폭: 100 }; // title = title이라는 속성 // 나머지 = 나머지 속성이 포함된 객체 let {title, ...rest} = 옵션; // 이제 title="메뉴", 나머지={높이: 200, 너비: 100} 경고(휴식.높이); // 200 경고(rest.width); // 100
let
없으면 알겠어
위의 예에서 변수는 할당에서 바로 선언되었습니다: let {…} = {…}
. 물론 let
없이 기존 변수를 사용할 수도 있습니다. 하지만 문제가 있습니다.
이것은 작동하지 않습니다:
제목, 너비, 높이를 지정하십시오. // 이 줄에 오류가 있습니다 {제목, 너비, 높이} = {제목: "메뉴", 너비: 200, 높이: 100};
문제는 JavaScript가 기본 코드 흐름(다른 표현식 내부가 아님)의 {...}
코드 블록으로 처리한다는 것입니다. 이러한 코드 블록은 다음과 같이 명령문을 그룹화하는 데 사용될 수 있습니다.
{ // 코드 블록 메시지 = "안녕하세요"; // ... 경고( 메시지 ); }
따라서 여기서 JavaScript는 코드 블록이 있다고 가정하므로 오류가 발생합니다. 대신 우리는 구조 분해를 원합니다.
JavaScript가 코드 블록이 아니라는 것을 보여주기 위해 표현식을 괄호 (...)
로 묶을 수 있습니다.
제목, 너비, 높이를 지정하십시오. // 이제 알았어 ({제목, 너비, 높이} = {제목: "메뉴", 너비: 200, 높이: 100}); 경고( 제목 ); // 메뉴
객체나 배열에 다른 중첩된 객체와 배열이 포함되어 있는 경우 더 복잡한 왼쪽 패턴을 사용하여 더 깊은 부분을 추출할 수 있습니다.
아래 코드에는 options
에 속성 size
에 또 다른 객체가 있고 속성 items
에 배열이 있습니다. 할당 왼쪽의 패턴은 값을 추출하는 것과 동일한 구조를 갖습니다.
옵션 = { 크기: { 너비: 100, 높이: 200 }, 항목: ["케이크", "도넛"], 추가 : 사실 }; // 명확성을 위해 할당을 여러 줄로 나누어 구조 분해 허락하다 { size: { // 여기에 크기를 입력합니다. 너비, 키 }, items: [item1, item2], // 여기에 항목을 할당하십시오 title = "Menu" // 객체에 존재하지 않음(기본값이 사용됨) } = 옵션; 경고(제목); // 메뉴 경고(너비); // 100 경고(높이); // 200 경고(항목1); // 케이크 경고(항목2); // 도넛
왼쪽 부분에 없는 extra
를 제외한 options
객체의 모든 속성은 해당 변수에 할당됩니다.
마지막으로 기본값에서 width
, height
, item1
, item2
및 title
갖게 됩니다.
대신 콘텐츠를 사용하므로 size
및 items
에 대한 변수는 없습니다.
함수에 많은 매개변수가 있는 경우가 있는데, 그 중 대부분은 선택사항입니다. 이는 특히 사용자 인터페이스에 해당됩니다. 메뉴를 생성하는 함수를 상상해 보세요. 너비, 높이, 제목, 항목 목록 등이 있을 수 있습니다.
다음은 이러한 함수를 작성하는 나쁜 방법입니다.
function showMenu(title = "제목 없음", 너비 = 200, 높이 = 100, 항목 = []) { // ... }
실생활에서 문제는 인수의 순서를 어떻게 기억하느냐이다. 일반적으로 IDE는 특히 코드가 잘 문서화되어 있는 경우 우리를 도우려고 노력하지만 여전히… 또 다른 문제는 기본적으로 대부분의 매개 변수가 괜찮은 경우 함수를 호출하는 방법입니다.
이와 같이?
// 기본값이 적합한 경우 정의되지 않음 showMenu("내 메뉴", 정의되지 않음, 정의되지 않음, ["Item1", "Item2"])
정말 추악해요. 더 많은 매개변수를 처리하면 읽을 수 없게 됩니다.
구조 파괴가 구출됩니다!
매개변수를 객체로 전달할 수 있으며, 함수는 이를 즉시 변수로 구조화합니다.
// 객체를 함수에 전달합니다. 옵션 = { title: "내 메뉴", 항목: ["항목1", "항목2"] }; // ...그리고 즉시 변수로 확장합니다. function showMenu({title = "제목 없음", 너비 = 200, 높이 = 100, 항목 = []}) { // 제목, 항목 – 옵션에서 가져옴, // 너비, 높이 - 기본값이 사용됨 경고( `${제목} ${너비} ${높이}` ); // 내 메뉴 200 100 경고(항목); // 항목1, 항목2 } showMenu(옵션);
중첩된 개체와 콜론 매핑을 사용하여 더 복잡한 구조 분해를 사용할 수도 있습니다.
옵션 = { title: "내 메뉴", 항목: ["항목1", "항목2"] }; 함수 showMenu({ 제목 = "제목 없음", width: w = 100, // 너비는 w로 갑니다. height: h = 200, // 높이는 h로 갑니다. items: [item1, item2] // 항목의 첫 번째 요소는 item1로, 두 번째 요소는 item2로 이동합니다. }) { 경고( `${제목} ${w} ${h}` ); // 내 메뉴 100 200 경고(항목1); // 항목1 경고(항목2); // 항목 2 } showMenu(옵션);
전체 구문은 구조 분해 할당과 동일합니다.
기능({ 수신속성: varName = defaultValue ... })
그런 다음 매개변수 개체의 경우 속성 incomingProperty
에 대한 varName
변수가 있고 기본적으로 defaultValue
있습니다.
이러한 구조 분해에서는 showMenu()
에 인수가 있다고 가정합니다. 기본적으로 모든 값을 원한다면 빈 객체를 지정해야 합니다:
showMenu({}); // 좋습니다. 모든 값은 기본값입니다. showMenu(); // 이렇게 하면 오류가 발생합니다.
{}
매개변수의 전체 객체에 대한 기본값으로 설정하여 이 문제를 해결할 수 있습니다.
function showMenu({ title = "메뉴", 너비 = 100, 높이 = 200 } = {}) { 경고( `${제목} ${너비} ${높이}` ); } showMenu(); // 메뉴 100 200
위 코드에서 전체 인수 객체는 기본적으로 {}
이므로 항상 구조화 해제할 항목이 있습니다.
구조 분해 할당을 사용하면 객체나 배열을 여러 변수에 즉시 매핑할 수 있습니다.
전체 객체 구문:
let {prop : varName = defaultValue, ...rest} = 객체
즉, 속성 prop
varName
변수에 들어가야 하며, 해당 속성이 없으면 default
을 사용해야 합니다.
매핑이 없는 개체 속성은 rest
개체에 복사됩니다.
전체 배열 구문:
let [item1 = defaultValue, item2, ...rest] = 배열
첫 번째 항목은 item1
로 이동합니다. 두 번째는 item2
로 들어가고 나머지는 모두 배열을 rest
로 만듭니다.
중첩된 배열/객체에서 데이터를 추출하는 것이 가능합니다. 왜냐하면 왼쪽이 오른쪽과 동일한 구조를 가져야 하기 때문입니다.
중요도: 5
우리는 객체를 가지고 있습니다:
사용자 = { 이름: "존", 년: 30 };
다음과 같은 구조 분해 할당을 작성하세요.
name
속성을 변수 name
에 추가합니다.
years
속성을 변수 age
에 넣습니다.
isAdmin
속성을 변수 isAdmin
에 추가(해당 속성이 없으면 false)
다음은 할당 후 값의 예입니다.
사용자 = { 이름: "John", 연도: 30 }; // 왼쪽에 있는 코드: // ... = 사용자 경고(이름); // 존 경고( 나이 ); // 30 경고( isAdmin ); // 거짓
사용자 = { 이름: "존", 년: 30 }; {이름, 연도: 연령, isAdmin = false} = 사용자를 설정합니다. 경고(이름); // 존 경고( 나이 ); // 30 경고( isAdmin ); // 거짓
중요도: 5
salaries
개체가 있습니다.
급여 = { "존": 100, "피트": 300, "메리": 250 };
가장 높은 급여를 받는 사람의 이름을 반환하는 함수 topSalary(salaries)
를 만듭니다.
salaries
비어 있으면 null
반환해야 합니다.
고소득자가 여러 명인 경우, 그중 하나를 반환하십시오.
PS 키/값 쌍을 반복하려면 Object.entries
및 구조 분해를 사용하세요.
테스트를 통해 샌드박스를 엽니다.
함수 topSalary(급여) { maxSalary = 0으로 놔두세요; maxName = null로 놔두세요; for(const [이름, 급여] of Object.entries(급여)) { if (maxSalary < 급여) { maxSalary = 급여; maxName = 이름; } } 최대 이름을 반환합니다. }
샌드박스에서 테스트를 통해 솔루션을 엽니다.