모던 자바스크립트 Deep Dive - 11. 원시 값과 객체의 비교 & 수업내용 추가 정리

지영·2021년 12월 10일
1

JavaScript

목록 보기
10/37
post-thumbnail

원시 값과 객체의 비교

  • 원시 타입의 값은 변경 불가능한 값이다
  • 객체 타입의 값은 변경 가능한 값이다
  • 원시 값을 변수에 할당하면 변수(확보된 메모리 공간)에는 실제 값이 저장된다
  • 객체타입을 변수에 할당하면 변수(확보된 메모리 공간)에는 참조 값이 저장된다
  • 원시 값을 갖는 변수를 다른 변수에 할당하면 원본의 원시 값이 복사되어 전달된다 -> 이를 값에 의한 전달이라 한다
  • 객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조 값이 복사되어 전달된다 -> 이를 참조에 의한 전달이라 한다

11.1 원시 값

  • 변경 불가능하다는 것은 변수가 아니라 값에 대한 진술이다
  • 상수는 재할당이 금지된 변수일 뿐이다. 따라서 상수와 변경 불가능한 값을 동일시 할 수 없다
  • 원시 값은 변경 불가 능한 값, 즉 읽기 전용 값이다
  • 원시 값을 할당한 변수에 새로운 원시 값을 재할당하면 새로운 메모리 공간을 확보하고 재할당한 원시 값을 저장한 후, 변수는 새롭게 재할당한 원시 값(메모리 주소)를 가리킨다
  • 원시값의 이러한 특성을 불변성이라 한다

11.1.2 문자열과 불변성

  • 1개의 문자는 2바이트의 메모리 공간에 저장된다
  • 문자열은 몇 개의 문자로 이뤄졌느냐에 따라 필요한 메모리 공간의 크기가 결정된다
  • 숫자 값은 1도, 100000도 동일한 8바이트가 필요하지만 문자열의 경우(실제와는 다르지만 단순 계산했을 때) 1개의 문자뤄 이뤄진 문자열은 2바이트, 10개의 문자열로 이뤄진 문자열은 20바이트가 필요하다

예제 11-03

var str = "hello";
str = "world";
  1. 첫 번째 문이 실행되면 문자열 'hello'가 생성되고 식별자 str은 문자열 'hello'가 저장된 메모리 공간의 첫 번째 메모리 셀 주소를 가리킨다
  2. 두번째 문이 실행되면 이전에 생성된 문자열 'hello'를 수정하는 것이 아니라 새로운 문자열 'world'를 메모리에 생성하고 식별자 srt은 이것을 가리킨다
  • 문자열 'hello'와 'world'는 모두 메모리에 존재한다
  • 식별자 str은 문자열 'hello'를 가리키고 있다가 문자열 'world'를 가리키도록 변경되었을 뿐이다

유사배열 객체

  • 마치 배열처럼 인덱스로 프로퍼티 값에 접근할 수 있고 length프로퍼티를 갖는 객체를 말한다
  • 문자열은 마치 배열처럼 인덱스를 통해 각 문자에 접근할 수 있으며, lenght 프로퍼티를 갖기 때문에 유사 배열객체이고 for문으로 순회할 수 있다

예제 11-05

  • 생성된 문자열은 읽기 전용 값으로서 변경할 수 없다
  • 원시 값은 어떤 일이 있어도 불변한다 데이터의 신뢰성을 보장한다
  • 변수에 새로운 문자열을 재할당하는 것은 물론 가능하다. 이는 기존 문자열을 변경하는 것이 아니라 새로운 문자열을 새롭게 할당하는 것이기 때문이다

11.1.3 값에 의한 전달

  • 변수에 원시 값을 갖는 변수를 할당하면 할당받는 변수에는 할당되는 변수의 원시 값이 복사되어 전달된다
  • 이를 값에 의한 전달이라 한다
  • 둘은 동일한 원시 값을 갖지만 두 변수의 원시 값은 다른 메모리 공간에 저장된 별개의 값이다
  • 엄격하게 표현하면 변수에는 값이 전달되는 것이 아니라 메모리 주소가 전달된다.이는 변수와 같은 식별자는 값이 아니라 메모리 주소를 기억하고 있기 때문이다
  • 식별자메모리 주소에 붙인 이름이라고 할 수 있다

    이처럼 값에 의한 전달도 사실은 값을 전달하는 것이 아니라 메모리 주소를 전달한다. 단, 전달된 메모리 주소를 통해 메모리 공간에 접근하면 값을 참조할 수 있다


11.2 객체

  • 객체는 원시 값과 같이 확보해야 할 메모리 공간의 크기를 사전에 정해 둘 수 없다

11.2.1 변경 가능한 값

  • 객체(참조) 타입의 값, 즉 객체는 변경 가능한 값이다
  • 객체를 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면 참조 값에 접근할 수 있다
  • 참조 값은 생성된 객체가 저장된 메모리 공간의 주소 그 자체이다
  • 변수는 참조 값을 통해 객체에 접근할 수 있다

원시 값을 할당한 변수를 참조하면 메모리에 저장되어 있는 원시 값에 접근한다.
하지만 객체를 할당한 변수를 참조하면 메모리에 저장되어 있는 참조 값을 통해 실제 객체에 접근한다.

  • 객체는 이러한 구조로 인해 여러개의 식별자가 하나의 객체를 공유할 수 있다

얕은 복사와 깊은 복사

  • 객체를 프로퍼티 값으로 갖는 객체의 경우 얕은 복사한 단계까지만 복사하는 것을 말하고 깊은 복사객체에 중첩되어 있는 객체까지 모두 복사하는 것을 말한다
  • 얕은 복사와 깊은 복사로 생성된 객체는 원본과는 다른 객체이다. 즉 원본과 복사본은 참조 값이 다른 별개의 객체이다
  • 하지만 얕은 복사는 객체에 중첩되어있는 객체의 경우 참조 값을 복사하고
  • 깊은 복사는 객체에 중첩되어있는 객체까지 모두 복사해서 원시 값처럼 완전한 복사본을 만든다

    원시 값을 할당한 변수를 다른 변수에 할당하는 것을 깊은 복사
    객체를 할당한 변수를 다른 변수에 할당하는 것을 얕은 복사라고 부르는 경우도 있다


11.2.2 참조에 의한 전달

  • 객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조 값이 복사되어 전달된다. 이를 참조에 의한 전달이라 한다

    결국 값에 의한 전달참조에 의한 전달식별자가 기억하는 메모리 공간(식별자의 주소값)에 저장되어 있는 값을 복사해서 전달한다는 면에서 동일하다


원시 값과 객체의 비교

  • 원시 타입과 객체 타입의 가장 큰 차이점 → 변경한 값인지 변경이 가능하지 않은 값인지(mutable한지 안한지)

mutable(뮤터블)

  • 변경이 가능한 객체
  • 최초 생성 이후에 자유롭게 값의 변경, 추가, 삭제 등이 가능하다
  • mutable한지 보려면 const 를 사용해보면 된다
    • const 에 객체를 넣어도 객체의 프로퍼티를 바꿀 수 있지만 , 원시 타입의 경우 재할당 할 수 없다
  • 객체도 메모리(주소값)을 가진다
  • 변수의 메모리에 객체가 저장된 메모리의 주소값(참조 값)이 담긴다
  • 이 주소값을 참조해서 객체들의 프로퍼티에 접근할 수 있다
const 식별자 = { };
  • 아래의 코드에서 a변수에 들어가 있는 것은 객체가 저장된 메모리의 주소값(참조 값)이 들어가 있다
// 객체의 메모리 주소값 -> 참조 -> 참조 타입
const a = { a: 1 };
a.a = 20;
console.log(a);

// { a: 20 };
  • 변수에는 말 그대로 주소값(참조값)만 들어가 있는 것
  • 주소값(참조 값)을 보고 메모리의 위치를 파악해서 접근한다
  • 객체를 새로 대입하는 것이 아닌 이상 재할당이 가능하다 (const로 선언했다 할지라도)
    • const로 변수를 선언한 상태에서 재할당이 가능하다면 참조타입
    • const로 변수 선언시 재할당이 불가능하다면 원시타입
  • const로 변수 선언시 새로운 객체를 재할당 하는 것은 불가하다
    → 새로운 주소'값'(참조 값)이기때문. const는 값을 재할당 할 수 없기때문에 새로운 값인 '객체'를 재할당 할 수 없다

11.2 객체

얕은 복사와 깊은 복사 → 중요!

  • 객체를 변수에 할당할 때는 해당 객체의 데이터가 위치한 주소 값(참조 값)을 할당한다
  • 객체를 복사할 때 객체 안에 프로퍼티가 객체인 경우, 객체 안의 프로퍼티인 객체도 주소 값(참조 값)을 가지고 있다
  • 이러한 경우 객체를 프로퍼티로 가진 객체를 어떻게 복사하는지에 따라 주소값이 깨질 수 있기도, 없기도 하다

얕은 복사

  • 제일 최상단에 객체를 하나 만들고 얕은 복사 할 객체 내부의 프로퍼티들만을 복사한다
  • 복사된 결과물(객체)의 프로퍼티와 원본 결과물(객체)의 프로퍼티는 복사된 것이기 때문에 동일한 주소 값(참조 값)을 가진다
  • 즉 내부의 참조 데이터는 유지가 된 것이다
  • ...obj은(spread 연산자)는 객체의 내부 프로퍼티를 그대로 복사 붙혀넣기 해준다
  • 두번째 방법으로는 object.assign(obj)으로도 obj객체의 내부 프로퍼티를 그대로 복사 붙혀넣기 해줄 수 있다 → object.assign은 두번째 인자로 객체를 또 하나 넣어 object.assign으로 생성된 객체를 수정해줄 수 있다
// 1.[shallow copy]
const obj = {
    a : 1,
    b : 2,
    c : {
        x : 'x',
        y : 'y',
        z : 'z',
    },
};

// 1-1. spread operator
const result1 = {
    ...obj,
};
  • 얕은 복사의 가장 큰 특징 → 껍데기만 ! 다르다 ! 왜? 새로운 객체니까!

배열의 얕은 복사

  • arr.slice(0) → 배열의 얕은 복사를 할 수 있다(더 알아보자)

깊은 복사

  • 깊은 복사는 객체 내부 프로퍼티까지 새로 만든다(복사한 객체와 주소값이 다르다)

JSON 으로 깊은 복사 하는 법

  • json은 JSON확장자(규약)을 지켜서 데이터 통신할 때 이런 모양으로 통신하자고 정한 것(usb 규격이 같은 것과 동일하다)
  • JSON.stringify()의 인자는 → 문자열로 리턴된다 → 더 이상 주소 값(참조 값)이 아니게 된다
  • JSON.parse()의 인자는 객체화 된다 → 문자열화 된 값을 다시 JSON.parse()의 인자로 넣어준다
  • 기존 객체와 -> JSON.stringify()의 인자로 주어 문자열화 된 객체를 → JSON.parse()의 인자로 다시 주어 다시 객체로 리턴된 객체는 기존의 객체와 다른 주소를 가지게 된다 (겉으로 보기에 프로퍼티가 동일해 보이나 다른 주소 값(참조 값)을 가지게 되었다)
  • 이미 문자열 타입이였다가 오브젝트로 리턴된 것이기 때문에 안에 있는 참조 데이터가 다 깨진 상태로 오는 것이다
  • 당연히 제일 껍데기도 새로운 주소 값(참조 값)을 가지고 → 객체 내부의 프로퍼티도 새로운 주소 값(참조 값)을 가진다
  • JSON.parse(JSON.stringify)와 같은 방법은 자주 사용하지는 않는다 → 애초에 이런 역할로 사용하기 위해 정의된 함수가 아니기 때문이다
  • 깊은 복사는 lodash라는 라이브러리를 많이 사용한다
    • cloneDeep(깊은 복사 하고싶은 객체)를 넣으면 알아서 깊은 복사를 해준다

11.2.2 참조에 의한 전달

  • 주소값을 넣는다고 생각하면 된다 → 얕은 복사라고 볼 수 있다 → 겉 껍데기는 다르지만 속의 프로퍼티는 같은 주소값을 공유하고 있기 때문에
var person = {
	name: 'Lee'
};

// 참조 값을 복사(얕은 복사)
var copy = person;

  • HTML에 box라는 클래스를 가진 div 10개가 있는데 → braket notation으로 아래와 같이 boxes[0]을 치환하면
  • 배열이 아니라 유사 배열 객체가 리턴된다
const boxes = document.querySelectorAll(".box"); // boxes에 유사 배열 객체가 할당된다

const elementIndex = 9;
const elementValue = boxes[elementIndex];

console.log(elementValue.textContent);

profile
천천히 운영되는 개발 블로그

0개의 댓글