#5 Spread + Rest

공부의 기록·2022년 1월 31일
0

JavaScript

목록 보기
13/16
post-thumbnail

Introduce

본 문서는 2022년 2월 2일 에 작성되었습니다.

강력한 Operator 인 ... 을 기반으로 작동하는
Spread, Rest Synytax (전개, 나머지 구문) 을 알아보겠습니다.

... 의 정식 명칭은 Spread Operator 입니다.

Theory

... Operator

... Operator 는 구조를 파괴한다는 점에서 Destructuring 과 유사합니다.

구조 분해 할당 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식입니다. Destructuring mdn

그렇지만 ... Operator 는 다음과 같은 기능을 담당해줍니다.

  1. 반복 가능한 속성을 가진 배열 등을 분해하는 기능
  2. 반복 가능한 속성을 가진 배열 등을 분해하여 키-값 쌍으로 변환하는 기능
  3. 가변인자로써의 기능

전개 구문을 사용하면 배열이나 문자열과 같이 반복 가능한 문자를 0개 이상의 인수 (함수로 호출할 경우) 또는 요소 (배열 리터럴의 경우)로 확장하여, 0개 이상의 키-값의 쌍으로 객체로 확장시킬 수 있습니다. ... Operator

위 내용에서 1,2 번을 ✅ Spread Syntax 에서
위 내용에서 3번을 ✅ Rest Syntax 에서 설명하도록 하겠습니다.


✅ Spread Syntax

전술한 바와 같이 Spread Operator 는
반복 가능한 속성을 가진 배열 등을 분해하는 기능 을 가지고 있습니다.

Spread Syntax, 전개 구문은 이러한 분해의 특성을 이용한 배열 및 객체의 가공 을 의미합니다.

따라서,
과거에는 다양한 클래스의 내장 함수로 구현했어야 했을 문제들을 Spread Syntax 로 해결할 수 있게 되었습니다.

1. 매개변수 전개

매개변수에 여러 변수를 배열로 넘겨주기 위해서는
과거에는 Array.prototype.apply() 를 사용했어야 했지만,
이제는 Spread Syntax 를 이용하여 복수의 변수를 전달할 수 있습니다.

매개변수를 주고 받을 때,
Spread, Rest Syntax 를 사용하며,
두 구문의 구체적인 차이는 다음과 같습니다.

1. Spread Syntax / 매개변수를 넘겨주기 위한...
2. Rest Syntax / 매개변수를 넘겨받기 위한...

정확한 차이는 ✅ Rest Syntax 를 확인해주세요.

// Before Spread Syntax
function argsFunc(x,y,z) {}
var array=[0,1,2];
argsFunc.apply(null,args);

// After Spread Syntax
function argsFunc(x,y,z) {}
let array=[0,1,2];
argsFunc(...args);

2. 배열 리터럴 전개

과거에는 다음과 같은 방법으로 배열을 복사할 수 있었습니다.

  1. Array.of(배열);
  2. Array.from(배열);
    Array, Set, Map > Array.Function > Copy 함수

또는 push(), splice(), concat() 등의 함수를 사용했어야 했습니다.
아니면 반복문을 통해서 함수를 더 지저분하게 쓰거나...

하지만 Array.from() 과 같이 Shallow Copy 하는 것이 가능해졌습니다.

let arr1=[10,20,30];
let arr2=[...arr1];

단, 다차원 배열일 경우 배열 안의 배열의 주소를 재사용하는 상황이 벌어집니다.

다음의 예시를 보면,
arr1.push(40) 은 기댓값대로 arr1 의 길이만 늘어나지만,
arr1[1].push(40) 은 기댓값과 달리 arr1, arr2 의 1번 배열이 모두 영향을 받습니다.

let arr1=[[10,20,30],[10,20,30],[10,20,30]];
let arr2=[...arr1];
arr1[1].push(40);
console.log(arr1[1]); // 10, 20, 30, 40
console.log(arr2[1]); // 10, 20, 30, 40

arr1.push(40);
console.log(arr1); // Array(3), Array(4), Array(3), 40
console.log(arr2); // Array(3), Array(4), Array(3)

이러한 Deep in Shallow Copy 는 Array.from() 으로 복사를 해도 동일하게 발생을 합니다. 따라서, 다음과 Array Destructuring 을 통해서 문제를 해결할 수 있습니다.

하지만 arr1[0],[1],[2] 는 a,b,c 와 연결되어 있음을 잊지 말아야 합니다.

const arr1=[[10,20,30], [10,20,30], [10,20,30]];
const [a,b,c]=arr1;
const arr2[[...a],[...b],[...c]];

3. 배열 연결

여러 배열 혹은 하나의 배열과 값을 연결하기 위해서
과거에는 Array.prototype.concat() 을 사용했어야 했지만,
이제는 Spread Syntax 를 이용하여 손쉽게 연결할 수 있습니다.

// Before Spread Syntax
const arr1=[10,20,30];
const arr2=[40,50,60];

const arr3=arr1.concat(arr2);
const arr4=arr1.concat([40]);
const arr5=arr1.concat(arr2).concat([70]);

// After Spread Syntax
const arr1=[10,20,30];
const arr2=[40,50,60];

const arr3=[...arr1,...arr2];
const arr4=[...arr1,40];
const arr5=[...arr1,...arr2,70];

4. 객체 리터럴 전개

객체를 복사하기 위해서 Object > Object.assign() 를 사용했어야 했지만,
이제는 Spread Syntax 를 이용하여 객체를 복사할 수 있습니다.

기본적으로 Shallow Copy 이며,
Object.assign() 은 setter(설정자) mdn 까지 복사하지만,
Spread Syntax 는 이를 복사하지 못한다.

아래의 예시들을 확인해보자

  1. 두 객체 => 하나의 객체
  2. 두 객체 => 하나의 객체 속의 두 객체
  3. 두 객체 => 하나의 배열 속의 두 객체
  4. 유효하지 않은 변수명의 객체

# 두 객체 => 하나의 객체

const user={
  name:"test1",
  age:26
};
const user2={
  name:"test2",
  height:200
};
const user3={ ...user1, ...user2 };
// name "test2"
// age 26
// height 200

# 두 객체 => 하나의 객체 속의 두 객체

function returnUser(name) {
  return ({ name });
}
function merge(...users) {
  return { ...users }
}

const user1=returnUser("unchaptered");
const user2=returnUser("hallow");
const userList=merge(user1,user2);
// 0:user1
// 1:user2

# 두 객체 => 하나의 배열 속의 두 객체

function returnUser(name) {
  return ({ name });
}
function merge(...users) {
  return [ users ]
}

const user1=returnUser("unchaptered");
const user2=returnUser("hallow");
const userList=merge(user1,user2);

# 유효하지 않은 변수명의 객체

const user1 = {"name": "unchaptered"};
const array = [...obj]; // TypeError: obj is not iterable

Destructuring > Object.Destructuring > 유효하지 않은 식별자명 에서 배운 기능을 활용해서 이를 Spread Syntax 에 적용할 수 있다.

const {"name":name} = {"name": "unchaptered"};
const array = [...obj];
// name unchaptered

✅ Rest Syntax

전술한 바와 같이 Spread Opertaor 는
반복 가능한 속성을 가진 배열 등을 분해하는 기능 을 가지고 있습니다.

Rest Syntax, 나머지 구문은 이러한 분해의 특성을 이용한 나머지 매개변수 설정 을 의미합니다.

1. 나머지 매개변수

함수에서 매개변수의 수를 확정할 수 없을 수 있습니다.

과거에는 arguments 객체를 사용했어야 했지만,
이제는 Rest Syntax 를 이용하여 복수의 변수를 받도록 지시할 수 있습니다.

// Before Rest Syntax
function testArguments(title, numArguments) {
  console.log(arguments[0]);
  console.log(arguments[1]);
  console.log(arguments[2]);
  console.log(arguments[3]);
  arguments.forEach((value,key)=>console.log(key+1, value));
}
testArguments("unchaptered","one","two","three");
// unchaptered
// one
// two
// three
// Uncaught TypeError: arguments.forEach is not a function

// After Rest Sytntax
function testRest(title, ...numList) {
  console.log(title);
  console.log(numList);
  numList.forEach((value,key)=>console.log(key+1, value));
}
testRest("unchaptered","one","two","three");
// unchaptered
// Array(3) [ "one", "two", "three" ];
// 1, one
// 2, two
// 3, three

# Rest Syntax 와 arguments 객체 차이

  1. arguments 객체는 실제 배열이 아니다.
  2. arguments 객체는 callee() 와 같은 추가 함수를 포함합니다.
  3. Rest Syntax 는 후속 매개변수만 포함하지만, arguments 는 전후의 매개변수를 모두 포함합니다.

Rest Syntax mdn > Rest Syntax vs arugments(object)

2. 객체 제어 > 요소 추가

Map.set() 이나 Object setter 처럼
객체에 특정 값을 새로 넣는 메서드를 만드는데 Rest Syntax 를 사용할 수 있습니다.

다음의 setter,설정자 와 Rest Syntax, 나머지 구문 의 예제 준비해봤습니다.

  1. Using setter to add Value
  2. Using setter to add Value with Destructuring
  3. Using Rest Syntax to add Default Value
  4. Using Rest Syntax to add Custom Value or Default Value
// Using setter to add Value
const user={
  username:"unchaptered",
  userpw: "password",
  set setLocation(location) {
    this.location=location;
  }
}
user.setLocation="Korea";

// Using setter to add Value with Destructuring
const user={
  username:"unchaptered",
  userpw: "password",
  set setLocation(location="Korea") {
    this.location=location;
  }
}
user.setLocation=undefined;

// Using Rest Syntax to add Default Value
const user={
  username:"unchaptered",
  userpw:"password",
};
function addElement({ location="Korea", ...others}) {
  return ({ location, ...others });
};

const newUser=addElement(user);

// Using Rest Syntax to add Custom Value or Default Value
const user={
  username:"unchaptered",
  userpw:"password",
};
function addElement(user, location="Korea") {
  return ({...user, location});
};

const newUser1=addElement(user);
const newUser2=addElement(user,"American");

# 요소 추가, 어떤 기준으로 선택해야 할까?

두 방식 모두 없던 요소를 추가하는 점 은 동일합니다.
또한 두 방식 모두 이미 있는 요소를 덮어씌울 수 있는 점도 동일합니다.

따라서 객체의 특성이 코드 전반에서 사용된다면,
OOP 의 이념을 따라서 setter() 를 설정하는 것이

단순하게 임의 요소를 추가하는 일회용 작용이라면,
코드의 효율성을 위해서 Rest Syntax 를 사용하는 것이 어떤가 생각해봅니다.

3. 객체 제어 > 요소 이름 변경

Destructuring > Object.Destructuring 을 이용하면
객체의 요소 이름을 변경할 수 있습니다.

하지만 한개 혹은 소수의 요소 명을 변경하기 위해서,
다음과 같이 Rest Syntax 를 활용할 수 있습니다.

해당 구문의 강점은 객체가 많은 요소를 가지고 있고 그 중 일부만 이름을 수정 해야하는 순간에 강하며 다음의 3번 코드와 같습니다.

  1. Using Object Destructuring Without Rest Syntax 1
  2. Using Object Destructuring Without Rest Syntax 2
  3. Using Object Destructuring With Rest Syntax
// Using Object Destructuring Without Rest Syntax - 1
let user={
  username:"unchaptered",
  userpw:"password",
};
function renameELement(user) {
  return ({ name:user.username, pw:user.userpw });
}
user=renameELement(user);

// Using Object Destructuring Without Rest Syntax - 2
let user={
  username:"unchaptered",
  userpw:"password",
};
function renameELement({username:name, userpw:pw) {
  return ({ name, pw });
}
user=renameELement(user);

// Using Object Destructuring With Rest Syntax
let user={
  username:"unchaptered",
  userpw:"password",
  email: "email",
  test1: "test1",
  test2: "test2",
  test3: "test3",
  test4: "test4",
};
function renameELement({username:name,userpw:pw, ...others}) {
  return ({name,pw,...others});
}
user=renameELement(user);

4. 객체 제어 > 요소 제거

Destructuring > Object.Destructuring 을 이용하면
객체의 요소를 제거할 수 있습니다.

하지만 한개 혹은 소수의 요소를 제거하는데,
다음과 같이 Rest Syntax 를 활용할 수도 있습니다.

해당 구문의 강점은 객체가 많은 요소를 가지고 있고 그 중 일부만 제거해야하는 순간에 강하며 다음의 3번 코드와 같습니다.

  1. Using Object Destructuring Without Rest Syntax 1
  2. Using Object Destructuring Without Rest Syntax 2
  3. Using Object Destructuring With Rest Syntax
// Using Object Destructuring Without Rest Syntax - 1
let user={
  username:"unchaptered",
  userpw:"password",
};
function removePassword(user) {
  return ({ username:user.username });
}
user=removePassword(user);

// Using Object Destructuring Without Rest Syntax - 2
let user={
  username:"unchaptered",
  userpw:"password",
};
function removePassword({username:user}) {
  return ({ user });
}
user=removePassword(user);

// Using Object Destructuring With Rest Syntax
let user={
  username:"unchaptered",
  userpw:"password",
  email: "email",
  test1: "test1",
  test2: "test2",
  test3: "test3",
  test4: "test4",
};
function renameELement({userpw, ...others}) {
  return ({...others});
}
user=renameELement(user);
profile
2022년 12월 9일 부터 노션 페이지에서 작성을 이어가고 있습니다.

0개의 댓글