ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 🍴 References VS Copying 🎎
    Web/JS30 2020. 11. 13. 17:27
    반응형

    배열이나 객체의 복사는 종종 문제가 되는 주제 중의 하나이다.

    복사해서 새로운 배열을 만든 줄 알았는데, 알고보니 원본을 참조하고 있어서

    각종 버그의 원인이 되는 경우가 적지 않다.

     

    오늘은 이런 참조(reference)와 복사(clone, copy)에 대해 알아보는 예제이다.

     

    1. Number, String, Boolean

    우선 Number, String, Boolean의 경우, 원본을 할당한 새로운 변수를 만들었을 때

    원본 변수의 값을 바꾼다고 해서, 새로운 변수의 값이 같이 바뀌진 않는다.

    let age = 100;
    let age2 = age;
    console.log(age, age2);
    age = 200;
    console.log(age, age2);
    
    let name = 'Wes';
    let name2 = name;
    console.log(name, name2);
    name = 'wesley';
    console.log(name, name2);

    원본을 그대로 할당한 변수를 만들더라도, 값만 동일한 완전히 새로운 변수가 만들어지는 거라고 보면 된다.

     

    2. 배열(Array)

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

    1) 할당

    우선, 배열을 위와 동일한 방식으로 새로운 변수에 할당해보자.

    const team = players;
    console.log(players, team);
    
    team[3] = 'Lux';
    console.log(players, team);

    그 결과는 다음과 같다.

    team이라는 변수를 새로 만들었지만, team은 배열이 아니다.

    team은 원본 배열인 players에 대한 참조(reference) 역할을 하는 변수일 뿐이다.

    그래서 원본 배열을 수정하거나 참조를 수정하거나

    두 경우 모두 결국에는 원본 배열을 수정하는 행위가 되어버린다.

     

    그럼, 배열의 참조가 아닌 배열의 복사본을 만들고 싶을 때는 어떻게 해야 할까?

    배열의 복사본을 만드는 방법은 여러 가지가 존재한다.

     

    2) slice()

    const team2 = players.slice();

    slice 메서드는 원본 배열을 건드리지 않은 채, 원본 배열의 일부분을 잘라 새로운 배열을 만드는 메서드이다.

    그래서 slice 메서드를 아무런 인자도 넣지 않고 사용하면

    원본 배열과 동일한 원소를 가진 새로운 배열을 만들 수가 있게 된다.

     

    3) concat()

    const team3 = [].concat(players);

    새로운 빈 배열을 만들고, 거기다가 players 배열을 이어붙여주는 방식이기 때문에

    원본과 원소는 동일하지만 원본과는 관계없는 새로운 배열이 만들어지게 된다.

     

    4) spread operator

    const team4 = [...players];

    역시나 새로운 배열을 만들어서 거기다가 players의 원소들을 채워넣는 방식.

     

    5) Array.from()

    const team5 = Array.from(players);

    원본 배열의 원소들을 바탕으로 새로운 배열을 만든다.

     

    3. 객체(Object)

    이제, 객체의 복사에 대해 알아보도록 하자.

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

    1) 할당

    const captain = person;
    captain.number = 99;

    객체 역시나, 단순히 원본 객체를 할당할 경우

    원본에 대한 참조를 가진 변수를 만드는 것에 불과하다.

     

    역시나, 객체를 단순히 참조하는 것이 아니라, 복사본을 만드는 방법이 필요하다.

     

    2) Object.assign()

    가장 흔히 사용되는 방법이다.

    const cap2 = Object.assign({}, person, { number : 99 });

    Object.assign(target, ...sources) 의 형태이다.

    sources의 요소들을 target 객체에다가 집어넣어서 새로운 객체를 만드는 방식이다.

     

    Object.assign의 첫 인자로 빈 객체를 줘서 비어있는 새로운 객체를 만들어준다.

    그리고 나서 나머지 인자들로 해당 객체에 넣고싶은 내용들을 객체의 형태로 넣어주면 된다.

    여기서는 person 객체와 동일한 요소를 가진 새로운 객체를 만들고, 거기다가 { number : 99 }를 추가했다.

     

    3) spread operator

    아직 완전히 확정되지는 않았고, 제안되고 있는 방법이다.

    빨리 spread operator로 편하게 객체를 다룰 수 있었으면 좋겠다.

    const cap3 = {...person};

     

     

    여기서 한 가지 알아야 할 점은, cap2와 cap3의 방법은 '얕은 복사'라는 점이다.

    즉, '한 단계'까지만 커버가 가능하다는 것.

    몇몇 sub-value(특정 키의 값으로 배열이나 객체가 들어있는 경우)들은 여전히 원본을 참조하고 있다.

    예를 들어보자.

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

    현재 wes라는 객체가 있는데, social이라는 키의 값으로 객체가 들어가있는 상태이다.

    이제 이 객체를 Object.assign으로 복사한 후, social 객체의 값을 수정해보자.

    const dev = Object.assign({}, wes);
    dev.name = 'Wesley';
    dev.social.twitter = '@coolman'

    그러면, 다음과 같은 결과가 출력된다.

    1단계보다 더 깊은 단계는 복사가 되지 않고 원본을 참조해버린다는 것을 알 수 있다.

    인터넷에 'object deep clone' 등으로 검색하면 관련된 함수나 라이브러리들을 찾을 수 있지만

    정말로 자원을 소모해가면서까지 deep clone을 꼭 사용해야 하는 상황인지는 생각해 볼 필요가 있다.

    생각보다 deep clone이 필요한 상황은 많지가 않다.

     

    ※ Deep Clone 편법 - JSON.parse(), JSON.stringify()

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

    객체를 문자열로 만들었다가 다시 객체로 만들기 때문에

    원본 객체와는 아예 상관없는 새로운 객체가 만들어진다.

    하지만, 성능적인 측면을 보장할 수 없기 때문에 편법으로 언급되는 방법들 중 하나이다.

     

    반응형

    'Web > JS30' 카테고리의 다른 글

    🤩 CSS Text Shadow Mouse Move Effect 😎  (0) 2020.11.18
    💿 LocalStorage & Event Delegation 📤  (0) 2020.11.17
    Slide In on Scroll 💻  (0) 2020.11.12
    Key Sequence Detection(KONAMI CODE) 🎮🎲  (0) 2020.11.11
    Custom HTML 5 Video Player 🎬🎥  (0) 2020.11.10

    댓글

Designed by Tistory.