Javascript_30_14

안녕하세요!

Derek 입니다! 😛

인트로 뭐라고 적어야할지 생각이 안나요. 음 새해 복 많이 받으세요 정도?
이 새 친구 정말 귀엽네요. 두둠칫띠.

이번 게시물은 Day 14 project를 다루며 간단한 javascript 의 기본적인 원리(?)를 알아보려 합니다!
시작해볼게요 :)




14. JavaScript References VS Copying

목표

배열 및 객체에 대해서 깊은 복사, 얕은 복사에 대해서 학습한다.

오늘은 다소 딱딱한 이론적인 부분을 다룰 예정입니다. 전체적인 코드 제시보다는, 하나하나 주제에 맞게 설명하도록 할게요.

01. Reference

첫 주제는 참조 에 관한 내용입니다.

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); // "wesley", "Wes" 출력

너무 간단하죠? 이건 그냥 넘어갈게요.

사실 아무 생각 없이 위에 코드를 보면 당연한데요, 아래 부분에서 약간 의아할 수 있습니다.

const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
const team = players;

team[3] = 'Lux';

이렇게 한다면, team 배열은, (4) ["Wes", "Sarah", "Ryan", "Lux"] 이런식으로, 값이 바뀔겁니다.

하지만 문제는 원본배열인 players 에도 변화가 생깁니다.

즉, players 의 3번째 인자도 Lux 로 바뀐다는 뜻이에요.

그 이유는, 새로 생긴 team 이라는 변수는 단지 players를 가르키는 포인터이기 때문에, 이런 일이 발생합니다.

새로운 배열을 생성하는 것이 아닌, 참조하는 포인터를 새로 생성한다.

라고 생각하면 될것 같아요.

02. Shallow copy - slice, concat, spread, Array

그렇다면 복사는 어떻게 하는 것일까요? 크게 4가지를 소개해보겠습니다.

1) slice

이는 배열을 복사하는 도구입니다.

const team2 = players.slice();

MDN 에 따르면,

slice(begin, end) 메서드는 어떤 배열의 begin부터 end까지(end 미포함)에 대한 얕은 복사본을 새로운 배열 객체로 반환합니다. 원본 배열은 바뀌지 않습니다.

라고 합니다. begin 부터, end - 1 까지 복사하여 새로운 배열을 반환합니다.

만약 아무런 인자가 포함되어 있지 않다면, 통째로 복사합니다. 즉, team2 에는 players 를 모두 복사하여 가지고 있겠죠!

2) concat

이는 배열, 문자열, 두 가지 타입에 대해서 사용할 수 있는 도구입니다.

MDN에 따르면,

The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.

기존 배열이나 문자열을 해치지 않고, 새로운 객체를 반환하죠. 여기에 자세히 나와있습니다.

const team3 = [].concat(players);

concat 함수는 항상 어떤 배열이나 문자열에 대해서 사용합니다. 즉, this에 대해서 그 뒤로 붙이겠다는 거죠.

위의 예시처럼 빈 배열에 대해서 사용하면, 복사와 다름 없는 것을 확인할 수 있어요.

3) spread

spread 전개 구문은 ES6 부터 사용된 친구입니다.

MDN에 따르면,

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

라고 하는데, 이는 여기에 나와있는 여러가지 예시들을 보고 참고하는 것이 좋을 것 같아요.

const team4 = [...players];
team4[3] = 'heeee hawww';
console.log(team4); // players 배열에 "heee hawww" 가 추가됨.

새로운 배열에 players 배열 원소를 하나하나 뿌려준다는 느낌으로 이해하면 될 것 같습니다.

4) Array from()

간단하게 함수 하나로도 복사가 가능합니다.

const team5 = Array.from(players);

MDN 에 따르면, Array.from의 정의는 다음과 같습니다.

The Array.from() static method creates a new, shallow-copied Array instance from an array-like or iterable object.

새로운 배열을 만드는 함수네요!

배열을 단순히 복사하기도 하지만, string 이나 Map 등에서도 다양하게 활용되는 것 같아요. 여기에 자세한 예시들이 있습니다 :)


03. Object Copies

이번에는 배열이 아니라 객체를 살펴보겠습니다.

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

분명히 captain 에만 추가를 했는데, person 원본에도 number 속성이 99로 들어가있네요.

object 도 그냥 할당해버리면 참조되는 형식으로 설정되는 것을 확인할 수 있습니다. 그렇다면 위 4가지 방법처럼 새로운 객체를 반환하는 방법은 없을까요?

1) Object.assign()

Object.assign() 구문을 사용하면 됩니다.

const person = {
      name: 'Wes Bos',
      age: 80
    };
const cap2 = Object.assign({}, person, {  });
console.log(cap2); // {name: "Wes Bos", age: 80}

MDN 문법을 참고하면,

The Object.assign() method copies all enumerable own properties from one or more source objects to a target object. It returns the target object.

음.. 잘 와닿지는 않아요! targetsource을 붙이는 느낌인 것 같습니다 :)

Object.assign(target, ...sources)

즉, targetsource를 복사합니다.

const cap2 = Object.assign({}, person, {  });
  • 첫 번째 인자 {} : 이 빈 객체에 집어넣을겁니다!
  • 두 번쨰 인자 person : person 객체를 빈 객체(첫 번쨰 인자)에 넣을거에요!
  • 세 번쨰 인자 { } : 추가할 내용은 없습니다!

이런 방식이에요! 어렵진 않은것 같아요. 😊


그런데, 이 Object.assign() 친구도 shallow copy 입니다. 그 이유인 예시를 들어볼게요.

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

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

이렇게 하면, dev 에는 wes 객체가 복사되어 있습니다.

하지만, devsocial 인자를 건드려보면,

dev.social.twitter = "@Derek";

이 결과는 당연히 위 그림처럼 됩니다. 하지만! wes 객체도 변합니다.

이전에 참조처럼 바뀌던 현상이 또 일어난 것이 보이네요. 결국 더 깊숙한 정보는 바꾸지 못하는(?) 결과를 관찰 할 수 있었습니다.

이는 JSON 으로 해결이 가능해요.

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

새로생성된 dev2wes 객체를 통으로 문자열로 만드는 JSON.stringify 를 거쳐서 다시 객체화 시키는 JSON.parse 함수로 생성됩니다. 따라서 dev2의 더 깊숙한 정보도 바꾸어도 본체는 바뀌지 않게 됩니다!




오늘은 기본적인 얉은 복사와 깊은 복사에 대해서 맛보기로! 여러가지 예시를 정리해보았습니다.

틀린내용이나 수정할 내용이 있다면 언제든지 피드백 부탁드립니다!
감사합니다!🤗

좋은 웹페이지 즐겨찾기