[JavaScript 30] Day 14: Object and Arrays - Reference VS Copy

sunshani·2024년 1월 17일
0

JavaScript30 Challenge 🔥

목록 보기
10/10

오늘은 구현 연습이라기보다는 객체와 배열의 참조와 복사에 관하여 짚고 넘어가는 내용. 기본형과 참조형이 어떻게 다르게 동작하는지 강의 들으면서 복습할 겸 정리해보았다.

무엇을 배웠나.

원시값의 경우

// start with strings, numbers and booleans
let age = 100;
let age2 = age;
console.log(age, age2); // 100 100
age = 200;
console.log(age, age2); // 200 100

let name = "Wes";
let name2 = name;
console.log(name, name2); // Wes Wes
name = "wesley";
console.log(name, name2); // wesly Wes

원본값인 agename을 복사하여 age2name2를 만든 후, 원본값을 변경하면 복사된 값에는 영향을 미치지 않는다.

배열의 경우

// Let's say we have an array
const players = ["Wes", "Sarah", "Ryan", "Poppy"];

// and we want to make a copy of it.
const team = players;

console.log(players, team);
// ["Wes", "Sarah", "Ryan", "Poppy"] ["Wes", "Sarah", "Ryan", "Poppy"]

잘 복사가 된 것 같아보이지만..

// You might think we can just do something like this:
team[3] = 'Lux';

// however what happens when we update that array?
// now here is the problem!
console.log(players, team);
// ["Wes", "Sarah", "Ryan", "Lux"] ["Wes", "Sarah", "Ryan", "Lux"]

// oh no - we have edited the original array too!

// Why? It's because that is an array reference, not an array copy.
//They both point to the same array!

player 배열을 복사한 team 배열의 요소를 변경한 뒤 확인해보면 team 배열뿐만 아니라 player 원본 배열까지 변경이 되었다. WHY? 실은 teamplayer에 대한 복사가 아니라 참조였기 때문. 두 변수는 똑같은 주소값을 참조하고 있다.

그래서 어떻게 해야 우리가 원하는 ✌️복사✌️를 할 수 있을까.

// So, how do we fix this? We take a copy instead!
// one way
const team2 = players.slice();

// or create a new array and concat the old one in
const team3 = [].concat(players);

// or use the new ES6 Spread
const team4 = [...players];

const team5 = Array.from(players);

// now when we update it, the original one isn't changed
  1. slice() 메소드 이용 - 아무런 아규먼트도 전달하지 않으면 전체 배열이 복사가 된다.
  2. concat() 메소드 이용 - 빈 배열에 복사하고자 하는 배열을 아규먼트로 담은 concat()메소드를 이용하면 복사할 수 있다. concat()은 두 개 이상의 배열을 병합하는 데 사용되는 메소드.
  3. 스프레드 문법 이용 - 복사하고자 하는 배열을 스프레드 문법으로 말그대로 펼쳐주기.
  4. Array.from() 메소드 이용 - 아규먼트로 복사하고자 하는 배열 넣어주기.

위의 방법들을 사용하면 원본 배열을 변경시키지 않고 분리된 복사본을 만들 수 있다~

객체의 경우

// The same thing goes for objects, let's say we have a person object

// with Objects
const person = {
  name: "Wes Bos",
  age: 80,
};

// and think we make a copy:
const captain = person;

console.log(person, captain);
// { name: 'Wes Bos', age: 80 } { name: 'Wes Bos', age: 80 }

객체의 경우도 마찬가지다. 이렇게 복사가 잘 된 것 같아보여도..

captain.number = 99;

console.log(person, captain);
// { name: 'Wes Bos', age: 80, number: 99 } { name: 'Wes Bos', age: 80, number: 99 }

이렇게 새롭게 만들어진 객체에 프로퍼티를 추가해주면 배열과 마찬가지로 원본 객체에까지 영향을 끼친다. 이유도 똑같다. captainperson에 대한 복사가 아니라 참조였기 때문. 두 변수는 똑같은 주소값을 참조하고 있다.

객체에서는 어떻게 우리가 원하는 복사를 할 수 있을까!

// how do we take a copy instead?
const cap2 = Object.assign({}, person, { number: 99, age: 12 });

const cap3 = {...person};
  1. Object.assign() 메소드 이용 - Object.assign(target, ...sources)을 이용하면 target...sources의 값을 덮어씌우며 수정된 target이 리턴된다. 첫 번째 아규먼트로 빈 객체를, 두 번째 아규먼트로 복사하고자 하는 객체를 넣어주면 복사 가능. 위의 예시에서는 복사하며 수정하고자 하는 사항을 세 번째 아규먼트로 전달해주어 병합해주는 방식으로 사용되었다.
  2. 스프레드 문법 이용 - 객체를 이용한 스프레드 문법은 ES2018에서 도입되었다. 배열의 스프레드 문법과 동일하게 동작한다.

얕은 복사와 깊은 복사

주의할 점은 위의 복사 방법은 전부 얕은 복사라는 것. 중첩된 객체의 경우 한 단계 더 깊숙히 있는 객체까지는 복사가 안 된다.

// Things to note - 
// this is only 1 level deep both for Arrays and Objects. 
// lodash has a cloneDeep method, but you should think twice before using it.

const wes = {
  name: "Wes",
  age: 100,
  social: {
    twitter: "@wesbos",
    facebook: "wesbos.developer",
  },
};

const dev = Object.assign({}, wes);

console.log(wes);
// { name: "Wes", age: 100, social: { twitter: "@wesbos", facebook: "wesbos.developer"} }
console.log(dev);
// { name: "Wes", age: 100, social: { twitter: "@wesbos", facebook: "wesbos.developer"} }

wesdev에 잘 복사가 된 것 같아보이지만...

dev.social.facebook = "jishanshan";

console.log(wes);
// { name: "Wes", age: 100, social: { twitter: "@wesbos", facebook: "jishanshan"} }
console.log(dev);
// { name: "Wes", age: 100, social: { twitter: "@wesbos", facebook: "jishanshan"} }

ㅋㅋ.. dev만 수정하고 싶어도 원본 객체였던 wes까지 수정된다.
얕은 복사라 그렇다. 중첩된 객체에는 여전히 영향을 준다.

모든 중첩 객체까지 복사해오고 싶다면, 클론을 만들고 싶다면, 그러니까 "깊은 복사"를 하고 싶다면 몇 가지 옵션이 있는데 오늘 강의에 나온 방법은 JSON.parse()JSON.stringify()를 이용하는 방법이다.

const dev2 = JSON.parse(JSON.stringify(wes));

쉽게 말해 객체를 문자열로 변환시킨 다음 다시 객체로 만들어 주는 방식이다.

참고

profile
日新又日新 ☀️ 😎

0개의 댓글