html,css,js

[자바스크립트 중급] 나머지 매개변수(... rest parameters), 전개구문(Spread syntax)

코복이 2023. 5. 9. 12:42
728x90

1. 들어가며 : rest, spread는 왜 필요한가?

- '...'을 함수에 매개변수로 쓰면 rest, 객체나 배열에 쓰면 전개구문 spread

- 코드를 줄일 수 있다. (하나하나 다쓰거나, 반복문 쓰지 않아도 됨)

 

2. 함수의 대량 인수를 처리하는 방법은 2가지

1) arguments (이제 안씀)

  • 함수로 넘어온 모든 인수에 접근
  • 과거에는 arguments만 쓸 수 있었음 (화살표 함수 못씀)
  • es6 환경이면 rest 쓰자

2) 나머지 매개변수 (...rest)

  • 함수에 사용
  • 보통 매개변수가 1개면 인수 1개만 가져오는데 ... 붙이면 인수 다가져옴 또는 인수가 몇개이든 상관 없음
  • 나머지 인수를 다 가져와 배열로 반환
  • rest 사용시 주의 점
    •  ...은 마지막 매개변수에만 붙을 수 있음 e.g) functionN(a,b,c) 여기서 c에만 가능 
    • 한 함수에 두개 이상의 매개변수에 ... 붙을 수 없음.
// ... 나머지 매개변수(rest parameters)
function showName(...names){ //names 라는 매개변수 하나만 설정되어 있는데 인수 다가져옴)
    console.log(names)
}

showName();
showName('Mike');
showName('Mike', 'Sam', 'Jane');

📌예제1: 인수로 받는 모든 숫자를 다 더해보자

1) rest 를 활용한 forEach문

// ... 나머지 매개변수(rest parameters)
// 인수로 받은 것들 다 더하는 함수
function add(...num){
    let result = 0;
    num.forEach((num)=>{result += num});
    console.log(result);
}

add(1,2,3,4); // 10

2) rest 안쓰면? : 배열이 아니여서 forEach 가 성립이 안됨

// ... 나머지 매개변수(rest parameters)
// 인수로 받은 것들 다 더하는 함수
// ... 안쓰고 num으로만 했을 때
function add(num){
    let result = 0;
    num.forEach((num)=>{result += num});
    console.log(result);
}

add(1,2,3,4); // TypeError: num.forEach is not a function // forEach앞에 num이 배열이 아니여서 에러남

- 이렇게 배열을 따로 정의해줘야 함. 즉, 인수 자체를 대량으로 받는게 아닌 대량으로 들어있는 배열을 받아야 함.

let test = [1,2,3,4];

function add(num){
    let result = 0;
    num.forEach((num)=>{result += num});
    console.log(result);
}

add(test); // 10 // 이렇게 따로 배열로 정의해줘야 함. // 즉, 인수 자체를 배열로 받아야 함.

3) reduce 문 활용 (return을 잘 정의해주고 누적값 + 현재값 여기가 += for문과 다른 것 기억)

// 인수로 받은 것들 다 더하는 함수 (reduce 문)
function add(...num){
    let result = num.reduce((prev, curr)=>{return prev + curr},0);
    console.log(result);
}
add(1,2,3,4); // 10

📌예제2: rest 이용하여 생성자 함수를 만들어라 (this와 new 쓰는거 기억)

// ... 나머지 매개변수(rest parameters)
// rest 이용 유저 객체를 만들어주는 생성자 함수 만들어라.

function UserMaker(name, age, ...skill){
    this.name = name;
    this.age = age;
    this.skill = skill; // 사람마다 스킬의 수는 다르니까
}
const id1 = new UserMaker("Mike", 29, "ppt", "figma", "word");
const id2 = new UserMaker("Mike", 29, "eat", "pray", "love");
const id3 = new UserMaker("Mike", 29, "sleep");

console.log(id1); // UserMaker { name: 'Mike', age: 29, skill: [ 'ppt', 'figma', 'word' ] }
console.log(id2); // UserMaker { name: 'Mike', age: 29, skill: [ 'eat', 'pray', 'love' ] }
console.log(id3); // UserMaker { name: 'Mike', age: 29, skill: [ 'sleep' ] }
 

 

3. 전개구문 (Spread syntax) : ...변수명

  • 배열, 객체에 사용 가능 
  • ...변수명 으로 사용
  • 변수에 속한 모든 요소 가져옴
  • 변수의 요소들 중간에 들어가도 됨 (위치 자유로움)
  • 복제가 됨: A 객체를 전개구문으로 B 객체에 넣고 A에서 전달받은 B의 프로퍼티를 수정해도 A에는 영향이 없음
  • 배열에 중간에 추가하고 수정하는 것은 메서드를 써야하니 번거로운데 전개구문 쓰면 쉬움
  • e.g.) arr.push()//마지막 추가, arr.splice()//특정요소 지우거나 추가, arr.concat()//새로운 배열 추가
// ... 전개 구문 (배열)
let arr1 = [1,2,3]
let arr2 = [4,5,6];

const result = [...arr1, ...arr2];
const result2 = [0,...arr1, 0, ...arr2, 0];
console.log(result); // [ 1, 2, 3, 4, 5, 6 ]
console.log(result2); // [ 0, 1, 2, 3, 0, 4, 5, 6, 0 ]
 
// ... 전개 구문 (객체)
let user1 = {name: "carl", age: 29}
let newUser = {id: 1, ...user1, location: "seoul"}; // 중간에 전개구문 삽입

console.log(newUser);
// { id: 1, name: 'carl', age: 29, location: 'seoul' }

📌 예제3: 전개 구문의 편안함을 느껴봐라 (배열 편)

// ... 전개 구문 (배열)
// arr1 을 [4,5,6,1,2,3]으로

let arr1 = [1,2,3];
let arr2 = [4,5,6];

// 1번. 전개구문사용
arr1 = [...arr2,...arr1];
console.log(arr1); // [ 4, 5, 6, 1, 2, 3 ]


// 2번. for, unshift 사용하여 만들기
for(let i = arr2.length -1; i > -1; i--){
    arr1.unshift(arr2[i])
}
console.log(arr1); // [ 4, 5, 6, 1, 2, 3 ]


// 3번. forEach, unshift 사용하여 만들기
arr2.reverse().forEach((num)=>{
    arr1.unshift(num);
})
console.log(arr1); // [ 4, 5, 6, 1, 2, 3 ]

 

📌 예제4: 전개 구문의 편안함을 느껴봐라 (객체 편)

// ... 전개 구문 (객체)
// 여러 객체를 하나로 모으기 (user 객체에 모으기 + 배열들은 객체 하나에 때려 넣기)

let user = {name: "James", age: 29};
let info = {adress: "seoul"};
let fE = ["JS", "React"];
let lang = ["Korean", "Chinese"];

// 1번. 전개구문으로 만들기 (내 방법) //이건 키 값이 skill 이 아닌 fe, lang 으로 들어감
let skill = {fE: fE, lang: lang}
user ={...user, ...info, ...skill}
console.log(user);
{
  name: 'James',
  age: 29,
  adress: 'seoul',
  fE: [ 'JS', 'React' ],
  lang: [ 'Korean', 'Chinese' ]
}


// 2번. 전개구문으로 만들기 (앙마님 방법) // 이건 키 값이 skill로 들어가고 하나의 키에 벨류 다들어감.
user = {
    ...user,
    ...info,
    skill: [...fE, ...lang]
}
console.log(user)
{
  name: 'James',
  age: 29,
  adress: 'seoul',
  skill: [ 'JS', 'React', 'Korean', 'Chinese' ]
}


// 3번. 객체메서드 Object.assign 으로 만들기 (내 방법)
let skill = {fE: fE, lang: lang}
user = Object.assign({}, user, info, skill);
console.log(user);
{
  name: 'James',
  age: 29,
  adress: 'seoul',
  fE: [ 'JS', 'React' ],
  lang: [ 'Korean', 'Chinese' ]
}


// 4번. 객체메서드 Object.assign 으로 만들기 (앙마님 방법)
user = Object.assign({},
    user,
    info,
    {skill: []}, // 빈객체 만들어 forEach로 배열 돌며 push()
    );

fE.forEach((item)=>{
    user.skill.push(item);
});
lang.forEach((item)=>{
    user.skill.push(item);
})
console.log(user);
{
  name: 'James',
  age: 29,
  adress: 'seoul',
  skill: [ 'JS', 'React', 'Korean', 'Chinese' ]
}

 

 

 

4. 데이터 타입 (참조형 & 원시형)과 스프레드 연관성

 

1) 데이터 타입

원시형: string, number, boolean

참조형: 객체, 배열

 

 

2) 차이점

원시형은 값을 그대로 할당하지만

참조형은 그 값이 담긴 주소(포인터 라고 함)를 복사함

 

참조형 즉, 객체나 배열을 복사하고 싶으면 스프레드를 써야 함.

 

e.g.
const person = {name: "max", age: 20}
const person1 = person; // 이렇게 하면 주소만 복사함. 값은 person이 변경되는 대로 person1 값도 바뀌어 버림
const person1 = {...person} // 이렇게 가져오면 값을 다 가져온 또다른 객체가 됨.
728x90