원시 값과 객체

Noob·2022년 8월 31일

7가지의 데이터 타입은 크게 원시 타입과 객체 타입으로 구분한다.

  • 원시 값은 변경이 불가능하다. 객체의 값은 변경 가능하다.
  • 변수에 원시 값을 저장하면 메모리 공간에 실제 값이 저장된다. 객체는 참조 값이 저장된다.
  • 원시 값을 가진 변수를 다른 변수에 할당하면 원시 값이 복사가 된다. 객체는 참조 값이 복사가 된다. 즉 원본에 접근 할 수 있느냐 없느냐다.

원시 값

변경 불가능한 값

한번 생성된 원시 값은 읽기 전용값으로 변경이 불가능하다. 변수는 재할당을 통해 값을 '교체'가능하다. 상수도 재할당이 불가능하나 그냥 그런 변수라 생각하면 된다. 값은 그냥 값이다. 선언, 할당, 재할당 모두 메모리 공간의 주소가 변경된다.
이는 값의 변경, 상태 변경을 추적하기 용이하게 하기 위함이다.

문자열과 불변성

천천히 설명을 읽으면 원시 값이 왜 불변성을 가지는지 이해 가능하다.
원시 값을 저장하려면 메모리 공간의 크기를 결정해야 한다. 원시타입 별로 메모리 공간의 크키가 미리 정해져 있고 문자열 타입은 2바이트 숫자 타입은 8바이트다.
문자열은 문자당 2바이트의 메모리 공간에 저장된다.

let str = 'hi'

식별자 str에 'hi'가 메모리 공간에 저장되었다.

str = 'hello'

str에 'hello'문자열을 재 할당하면 같은 메모리 공간내 문자열이 바뀌는게 아니라 다른 메모리 주소로 hello가 저장되고 str식별자가 hello의 메모리 주소를 기억하게 된다.

문자열은 유사 배열 객체이므로 인덱스로 프로퍼티 값에 접근 가능하다.

let str = 'hello'
str[0]	//	'h'

근데 원시 값은 변경이 불가능하다 했다.

str[0] = 'T'
console.log(str)	// 'hello' 변경이 안되네?

이처럼 한번 생상된 문자열은 읽기 전용이다.

값에 의한 전달(메모리 주소의 전달)

변수에 원시 값을 가지는 변수를 할당하면 값이 복사가 되어 전달된다. 단 다른 메모리 공간에 저장된 별개의 값이다.

let x = 5
let y = x
console.log(x)		// 5
console.log(y)		// 5
y = 10
console.log(x)		// 5
console.log(y)		// 10	x의 원시값에 영향을 끼치지 않았다

객체

객체의 프로퍼티의 개수는 정해져 있지 않고 동적으로 추가 삭제가 가능하다. 따라서 메모리 공간의 크기를 사전에 정해 둘 수 없다.
자바스크립트는 클래스 없이 객체를 생성하여 동적으로 프로퍼티와 메서드를 추가 가능할 수 있다.
객체를 생성하고 프로퍼티에 접근 하는건 클래스를 통해 객체를 생성하는 방식이나 원시 값에 비교하면 비용이 많이 드는 일이다.
원시 값과 객체의 프로퍼티 접근 법은 다르다
따라서 자바스크립트 엔진에서는 프로퍼티에 접근하기 위해 동적 탐색 대신 히든 클래스라는 방식을 사용해서 성능을 보정했다.

히든클래스???
자바스크립트의 프로퍼티 접근 방식은 클래스 기반 객체지향 프로그래밍 언어에 비해 객체 생성 후 프로퍼티와 메서드를 수정 할 수 있지만 비용이 많이 들게된다 따라서 동적 탐색이 아닌 히든 클래스 방식을 사용해 객체 프로퍼티에 접근하는 방식을 사용하게 된다.

얕은 복사와 깊은 복사

여러개의 식별자가 하나의 객체를 공유 할 수 있다.
객체의 값만을 복사 할 수도 있고 객체 자체를 공유 할 수도 있다.
얕은 복사를 할경우 여러 식별자가 하나의 객체를 공유 하여 수정시 모든 식별자에 영향을 끼칠 수 있으므로 얕은 복사와 깊은 복사를 알아본다.

얕은 복사
const l = {x : {t : 1}, y : 2} // 변수 l은 객체에 접근할 참조값을 할당한다.
const l2 = {...l} // l2에 변수 l을 구조 분해 할당에서 Rest elements를 합니다.
console.log(l === l2)	//false
console.log(l2.x === l.x) // true
l2.y = 5	// 복사해온 1깊이의 객체값 수정
console.log(l.y)	//  처음 선언한 객체 l 의 값은 2 변하지 않아서 깊은 복사로 보인다.
l2.x.t = 5	// 복사해온 2깊이의 객체값 수정
console.log(l.x.t) // 5 깊이2의 객체값은 메모리주소가 같다는걸 알 수 있다. 여긴 얕은 복사다.
// 정리 - 스프레드 연산자나 객체가 할당된 변수를 변수에 할당 했을시 깊이에 따라 차이는 있겠지만 얕은 복사가 된다.(Object.assign() 등등 1깊이의 복사가 대부분)

깊은 복사
// 스프레드를 이용한 방법. 위에는 안된다고 했지만 값을 추출해서 할당하는 방식
const l = {x : {t : 1}, y : 2} 
const l2 = {...l, x: {...l.x}}

// lodash의 cloneDeep을 이용한 복사 Node.js 환경사용
const deep = require("lodash")
const l3 = deep.cloneDeep(l)
profile
나의 기록

0개의 댓글