오늘은 구현 연습이라기보다는 객체와 배열의 참조와 복사에 관하여 짚고 넘어가는 내용. 기본형과 참조형이 어떻게 다르게 동작하는지 강의 들으면서 복습할 겸 정리해보았다.
// 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
원본값인 age
와 name
을 복사하여 age2
와 name2
를 만든 후, 원본값을 변경하면 복사된 값에는 영향을 미치지 않는다.
// 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? 실은 team
은 player
에 대한 복사가 아니라 참조였기 때문. 두 변수는 똑같은 주소값을 참조하고 있다.
그래서 어떻게 해야 우리가 원하는 ✌️복사✌️를 할 수 있을까.
// 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
slice()
메소드 이용 - 아무런 아규먼트도 전달하지 않으면 전체 배열이 복사가 된다.concat()
메소드 이용 - 빈 배열에 복사하고자 하는 배열을 아규먼트로 담은 concat()
메소드를 이용하면 복사할 수 있다. concat()
은 두 개 이상의 배열을 병합하는 데 사용되는 메소드.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 }
이렇게 새롭게 만들어진 객체에 프로퍼티를 추가해주면 배열과 마찬가지로 원본 객체에까지 영향을 끼친다. 이유도 똑같다. captain
은 person
에 대한 복사가 아니라 참조였기 때문. 두 변수는 똑같은 주소값을 참조하고 있다.
객체에서는 어떻게 우리가 원하는 복사를 할 수 있을까!
// how do we take a copy instead?
const cap2 = Object.assign({}, person, { number: 99, age: 12 });
const cap3 = {...person};
Object.assign()
메소드 이용 - Object.assign(target, ...sources)
을 이용하면 target
에 ...sources
의 값을 덮어씌우며 수정된 target
이 리턴된다. 첫 번째 아규먼트로 빈 객체를, 두 번째 아규먼트로 복사하고자 하는 객체를 넣어주면 복사 가능. 위의 예시에서는 복사하며 수정하고자 하는 사항을 세 번째 아규먼트로 전달해주어 병합해주는 방식으로 사용되었다.주의할 점은 위의 복사 방법은 전부 얕은 복사라는 것. 중첩된 객체의 경우 한 단계 더 깊숙히 있는 객체까지는 복사가 안 된다.
// 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"} }
wes
가 dev
에 잘 복사가 된 것 같아보이지만...
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));
쉽게 말해 객체를 문자열로 변환시킨 다음 다시 객체로 만들어 주는 방식이다.