240814 TIL - JS 문법 종합반 wk3

LIHA·2024년 8월 14일
1

내일배움캠프

목록 보기
18/117
post-thumbnail

기본형과 참조형

  • JS의 원시형(기본형) 데이터타입은 총 6가지
    Number, String, Boolean, null, undefined, Symbol
    Symbol은 뭔지 모르겠지만 그런게 있는가보다. 와 검색해봤는데 더 모르겠어!
    -> 2015년 ES6로 올라가면서 새로 도입된 타입이라고.

  • 참조형의 근본은 Object - 배열, 함수, Date, RegExp, Map, Set 모두의 조상

  • 타입의 구분은 값의 저장방식과 불변성 여부. 기본형은 불변성이 있고 참조형은 불변성이 없다.
    이때의 불변성은 값을 재할당 못한다는 말이 아니다. 메모리 관점에서의 불변성이야!
    -> JAVA나 C와 다르게 JS의 숫자 메모리는 8byte로 고정이다.
    그러나 문자로 가면 또 다르다. 영문은 1, 한글은 2byte다.

▶메모리 관점에서의 불변성 - 선언과 할당

  • 식별자와 변수는 별개이다. 변수는 데이터고 식별자는 변수명이다. var a = 3; 에서 a는 식별자, 3이 변수인 것.

값을 바로 변수자리에 대입하지 않는 이유? 변환과 효율성 때문

1. 자유로운 데이터 변환
데이터가 더 커지면 현재 할당된 변수 영역만큼보다 더 필요한데, 할당된 메모리 뒤에 다른 데이터들이 들어와있는 경우 걔네를 다 밀어버려야 한다. 그렇게 길어질 때/짧아질 때마다 뒷 데이터들을 밀었다 땡겼다 하는 작업은 번거롭다!

▶ 변수가 저장된 주소값만 갖고 있다면 변수를 바꿔줘도 아무 상관 없다. 해당 값의 주소로만 갈아치워주면 되니까!
(위 이미지에서 데이터를 'test!' 에서 'test?'로 바꾸려면 변수 영역이 쥐고있는 주소값만 딴걸로 갈아치우면 되는 것!

2. 메모리 관리의 효율성
숫자 1 100만개를 바로 변수자리에 저장한다 가정하면 변수 영역에 필요한 메모리는 8바이트 * 100만 = 800만바이트
(+ 이 800만 바이트를 수정할 경우 변동되는 메모리 크기와 점유 영역 변화 등등의 문제...)

▶ 그러나 주소값만 저장하면 8바이트는 아닐 것. 변수 영역에 필요한 메모리를 2바이트라고 가정하면 2바이트 * 100만 = 200만바이트 + 데이터 영역은 8바이트 한개이므로 200만 8바이트 필요. (약 4분의 1)

그래서 기본형의 불변성이라는 것은요

이 이미지는 str = 'test!'; 를 메모리 영역에서 표현한 것인데, 불변성이란, 우리가 str = 'test?'; 로 고친다고 해서 실제로 메모리 상 5003호의 데이터에서 !를 지우고 ?로 고쳐주는 것은 아니란 얘기다. 우린 5003호의 데이터를 고칠 수 없다.
무조건 새로운 영역에 'test?' 라는 값을 새로 써넣고 그 새로운 주소값만을 str의 데이터 자리에 써주는 것 뿐이지. (마치 쿠팡 로켓배송이 다 같이 시켰지만 반드시 따로따로 한 봉투씩 보내주는 것처럼)
그래서 기본형은 불변성이 있다고 표현한다.

그러면 참조형의 가변성이라는 것은요?

참조형은 기본적으로 객체 타입이라 참조하는 주소값까지 바뀌어버릴 수 있다.
문제는 그렇게 참조하는 주소값이 바뀌면, 사본을 떠가서 사본의 데이터를 바꾸려는데 원본의 데이터가 같이 바뀌어버리는 불상사가 일어난다.

이게 얼마나 중요하냐면, 원장 튜터님이 투장이 되어버린다. 이거봐요! 진짜야!

그 와중에 궁금증 - 복사는 그냥 원본의 주소를 가리키면 안되나요?

안 된다. 그건 복사가 아니라 내 하위에 그 객체가 있고 내 하위 중에서 걔를 골라 가리키겠다는 말이 되기 때문.

그래서 우리는 가변한 것의 위험성을 불변한 것 처럼 바꾸려는 노력을 해야한다.
그 노력의 결과물 중 하나가 바로 얕은복사. 그러나 문제점 -> 한 뎁스만 들어가기 때문에 중첩객체인 경우 제대로 복사되지 않아 또 가변성의 위험이 있다!

// 얕은복사의 원뎁스 복사를 극복하려면 
//내가 알아서 n차 카피까지 떠줘야 한다는 말이 된다!

var copyObject = function (target) {
	var result = {};

	// for ~ in 구문을 이용하여, 객체의 모든 프로퍼티에 접근할 수 있습니다.
	// 하드코딩을 하지 않아도 괜찮아요.
	// 이 copyObject로 복사를 한 다음, 복사를 완료한 객체의 프로퍼티를 변경하면
	// 되겠죠!?
	for (var prop in target) {
		result[prop] = target[prop];
	}
	return result;
}

var user = {
	name: 'wonjang',
	urls: {
		portfolio: 'http://github.com/abc',
		blog: 'http://blog.com',
		facebook: 'http://facebook.com/abc',
	}
};

// 1차 copy
var user2 = copyObject(user);

// 2차 copy -> 이렇게까지 해줘야만 해요..!!
user2.urls = copyObject(user.urls);

user.urls.portfolio = 'http://portfolio.com';
console.log(user.urls.portfolio === user2.urls.portfolio);

user2.urls.blog = '';
console.log(user.urls.blog === user2.urls.blog);

결론은 버킹검 깊은복사. 골수까지 쓸어주는 재귀함수 호출

그래서 결국 답을 찾아낸 우리는

// 그러니 깊은복사를 해 줍시다 
// null이 아닌 객체는 골수까지 쓸어담아주는 함수를 만들자
// 그리고 return에 자기 자신을 걸어 재귀함수로 만들어주면 완성

var copyObjectDeep = function(target) {
	var result = {};
	if (typeof target === 'object' && target !== null) {
		for (var prop in target) {
			result[prop] = copyObjectDeep(target[prop]);
		}
	} else {
		result = target;
	}
	return result;
}
//결과 확인
var obj = {
	a: 1,
	b: {
		c: null,
		d: [1, 2],
	}
};
var obj2 = copyObjectDeep(obj);

obj2.a = 3;
obj2.b.c = 4;
obj2.b.d[1] = 3;

console.log(obj);
console.log(obj2); 

이렇게 깊은 복사로 골수까지 쓸어주어 원뎁스의 위험성을 해결하고 모든 값을 가져올 수 있게 되었다.

호이스팅이 뭔데요? 레코드를 낚는 어부가 되는 것

야호! 하고 한가롭게 낚시 호이스팅을 하고 있는 나 (치고 너무 말랐다)
호이스팅이 뭔지 간략하게 알려준 블로그

profile
갑자기 왜 춤춰?

0개의 댓글