JavaScript

[모던자바스크립트] 원시값과 객체의 비교

jjin502 2022. 7. 4. 20:31
  • 자바스크립트는 크게 두 타입으로 나뉜다.
  • 원시 값 : 원시 타입의 값, 즉 변경 불가능한 값
  • 객체 값 : 객체 타입의 값, 즉 변경 가능한 값

그렇다면 이 두 개의 차이점은 무엇인가 .. ?

  •  저장에서 차이   
    • 원시 값을 변수에 할당하면 변수(확보된 메모리 공간)에는 실제 값 저장됨.
    • 반면, 객체를 변수에 할당하면 변수(확보된 메모리 공간)에는 참조 값 저장됨. 
  •  전달에서 차이
    • 에 의한 전달 : 원시 값을 갖는 변수를 다른 변수에 할당하면, 원시 값이 복사되어 전달됨.
    • 참조에 의한 전달 : 객체를 가리키는 변수를 다른 변수에 할당하면 참조 값이 복사되어 전달.

원시 값

변경 불가능한 값

  • 한 번 생성된 원시 값은 읽기 전용 값으로서 변경 할 수 없다.

주의

  • 변경 불가능하다는 것은 변수가 아니라 '값'이다.
  • 변수와 값은 전 ~ 혀 다른 의미임을 잊지말것 !!

그렇다면 상수는 뭐죠 .. ?

// const 키워드를 사용해 선언하는 변수는 재할당 급지.
const o = {};

// const 키워드를 사용해 선언한 변수에 할당한 원시 값은 변경할 수 없음
// 하지만, const 키워드를 사용해 선언한 변수에 할당한 객체는 변경 할 수 있음 
o.a = 1;
console.log(o);

문자열과 불변성

  • 문자열 : 0개이상의 문자로 이뤄진 집합.
  • 불변성 : 변수 값을 변경하기 위해 원시 값을 재할당하면 새로운 메모리 공간을 확보하고 재할당한 값을 저장한 후, 변수가 참조하던 메모리 공간의 주소를 변경하는 값의 특성
    var str1 = ""; // 0개의 문자로 이뤄진 문자열
    var str2 = "Hello"; // 5개의 문자로 이뤄진 문자열
  • 1개 문자열 크기 = 2바이트

자바스크립트 특 : 개발자 편의를 생각함

  • 원시 타입인 문자열 타입을 제공함 ㅎㅎ
    var str = "Hello";
    var str = "world";

    // 값이 덮어쓰기가 되는 것이 아닌, 새로 메모리를 확보 후 가리키는 방향을 변경함.

유사 배열 객체

  • 마치 배열 처럼 인덱스로 프로퍼티 값에 접근할 수 있고,length 프로퍼티를 갖는 객체
  • 즉, 배열과 유사하게 각 문자에 접근할 수 있다. 
// 11-4
    // 문자열과 불변성
    // 문자열 - 유사 배열 객체이면서 이터러블임
    
    var str = "string";

    console.log(str[0]);
    console.log(str.length);
    console.log(str.toUpperCase());

그런데 .. 변하지 않는다고해서 좋은게 있나요 .. ?

 

    // 문자열은 원시값이므로 값을 변경 할 수 없다.
    var str = "string";

    str[0] = "S";
    console.log(str);

    // 불변 -> 데이터의 신뢰성 보장
  • 당근 ~ 신뢰성을 보장한다구 !!

값에 의한 전달

  • 변수에 원시값을 갖는 변수를 할당받는 변수에는 할당되는 변수의 원시값이 복사되어 전달됨
    
    // 문자열은 원시값이므로 값을 변경 할 수 없다.
    var sc = 80;
    var copy = sc;

    console.log(sc);
    console.log(copy);

    sc = 100;

    console.log(sc);
    console.log(copy);

하지만 그들의 차이는 있다 .. 그것은 바로 ..!!

 

    var sc = 80;
    var copy = sc;

    console.log(sc);
    console.log(copy);

    console.log(sc === copy);
    // sc copy 값은 같음 그러나 메모리 주소가 다름
  • 그렇다 메모리 주소가 다르다

 

    var sc = 80;
    var copy = sc;

    console.log(sc);
    console.log(copy);

    console.log(sc === copy);
    // sc copy 값은 같음 그러나 메모리 주소가 다름

    sc = 100;
    console.log(sc, copy);
    console.log(sc === copy);

    // sc와 copy가 별개라는 것을 보여줌

값에 의한 전달의 비밀

엄격히 표현하자면, 메모리 주서가 전달되는 것임.

왜죠 ..?

식별자는 값이 아니라 메모리 주소를 기억하기 때문이죠 ...

    // 식별자로 값을 구별해서 식별한다
    // == 식별자가 기억하고 있는 메모리주소를 통해 메모리 공간에 저장된 값에 접근할 수 있다.
    // == 식별자 : 메모리 주소에 붙인 이름
    var sc = 80;
    // 식별자 sc는 메모리 공간에 저장된 숫자 80을 식별 할 수 있음

    // 11-10
    var cpy = sc;
    // 위 예제 경우 sc는 식별자 표현식으로서 숫자 값 80으로 평가
    // 1. 새로운 80을 생성(복사)해서 메모리주소를 전달. -> 할당 시점에 두 변수가 기억하는 메모리 주소가 다름
    // 2. sc변수값 80을 메모리 주소를 그대로 전달. -> 할당 시점에 두 변수가 기억하는 메모리 주소가 같음.

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

결론

두 변수의 원시 값은 서로 다른 메모리 공간에 저장된 별개의 값이 되어 어느 한 쪽에서 재할당을 통해 값을 변경하더라도 서로 간섭할 수 없다.

 

객체

변경 가능한 값

// 11-11
    // 변경 가능한 값 == 객체(참조) 타입의 값

    var person = {
      name: "Sim"
    };

    // 참조 값 : 생성된 객체가 저장된 메모리 공간의 주소 그 자체임
    // 변수는 참조값을 통해 객체에 접근할 수 있음
  • 참조값 : 객체를 할당한 변수에 생성된 객체가 실제로 저장된 메모리 공간의 주소가 저장되어있는 값.
  • 변수는 참조값을 통해 객체에 접근할 수 있다.
// 11-12
    // 할당이 이뤄지는 시점에 객체 리터럴이 해석되고, 그 결과 객체가 생성
    var person = {
      name: "Sim"
    };

    // person변수에 저장되어 있는 참조 값으로 실제 객체에 접근한다.
    // 객체 = 변경 가능한 값
    // 재할당 없이 객체를 직접 변경 할 수 있음.
    // 재할당 없이 CRUD 가능
    console.log(person);
    // {name: "Sim"}
// 11-13
    // 할당이 이뤄지는 시점에 객체 리터럴이 해석되고, 그 결과 객체가 생성
    var person = {
      name: "Sim"
    };

    // 프로퍼티 값 갱신
    person.name = "Lee";

    // 프로퍼티 값 동적 생성
    person.address = "Ulsan";

    console.log(person);

주의

  • 🚨원시 값과 다르게 여러 개의 식별자 하나의 객체 공유할 수 있음.

얕은 복사 vs 깊은 복사

// 11-14
    // 얕은 복사 : 한 단계까지만 복사
    // 깊은 복사 : 객체에 중첩되어있는 객체까지 모두 복사하는 것을 말함
    const o = { x: { y: 1 } };

    // 얕은 복사
    const c1 = { ...o };
    console.log(c1 === o);
    console.log(c1.x === o.x);

    // lodash cloneDeep을 사용한 깊은 복사
    // npm install lodash 설치 후 노드 환경에서 실행
    const _ = require("loadash");

    // 깊은 복사
    const c2 = _.cloneDeep(o);
    console.log(c2 === o);
    console.log(c2.x === o.x);
// 11-15
    const v = 1;

    // 깊은 복사
    const c1 = v;
    console.log(c1 === v);

    const o = { x: 1 };

    // 얕은 복사
    const c2 = o;
    console.log(c2 === o);

참조에 의한 전달

// 11-16
    // 참조에 의한 전달 : 객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조값이 복사되어 전달됨
    var person = {
      name: "Sim"
    };

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

    // 두 개의 식별자가 하나의 객체를 공유한다.
    // 서로 영향을 주고 받음
// 11-17
    // 참조에 의한 전달 : 객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조값이 복사되어 전달됨/
    var person = {
      name: "Sim"
    };

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

    console.log(copy === person);

    // 객체 변경
    copy.name = "Kim";
    // 객체 변경
    person.address = "Ulsan";

    console.log(person);
    console.log(copy);
// 11-18
    // 참조에 의한 전달 : 객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조값이 복사되어 전달됨
    var person1 = {
      name: "Sim"
    };

    var person2 = {
      name: "Sim"
    };

    console.log(person1 === person2);
    console.log(person1.name === person2.name);

결론 ... 

  • 원시 값과 객체 값은 확연한 차이가 있으며 헷갈려서는 안됨 !!