[Day14] Javascript 원시타입, 참조 타입

Validator·2023년 6월 30일
0

if

if (조건식) {
   // statement1
} else if(조건식) {
   // statement2
} else {
   // statement3
}
  • if 안에 있는 조건식이 참인 경우 해당하는 if문, else if문을 실행 한다.
  • 조건문(if, else if)에 모두 해당하지 않으면 else에 있는 statement3 이 실행된다.
  • else if에는 갯수 제한이 따로 없다. (아주 정교하고 복잡한 조건식을 구성할 수도 있다!)
  • else를 꼭 써줄 필요는 없다.

조건식에서 거짓으로 취급하는 값

1) false
2) undefined
3) null
4) 0
5) NaN
6) the empty string ("")

조건문에서 사용할 수 있는 비교 연산자

1) ===, !== : 한 값이 다른 값과 같거나 다른지 판단 한다. (이는 데이터 타입이 같은지까지 판단한다!, 보통은 ==대신 ===을 사용하게된다)
2) <, > : 한 값이 다른 값보다 작은지 큰지 판단 한다.
3) <=, >= : 한 값이 다른 값보다 작거나 같은지, 크거나 같은지 판단 한다.
4) ==, != : 값이 같은지 확인한다. 다만 데이터 타입까지는 비교하지 않기에
문자열 2와 숫자2를 같다고 판정해주게 된다.

var score = 96;
if (score >= 90){
	console.log("점수 : A"); // 해당문 실행
}else if (score >= 80){
	console.log("점수 : B");
}else if (score >= 70){
	console.log("점수 : C");
}else if (score >= 60){
	console.log("점수 : D");
}else {
	console.log("점수 : F");
}

if문의 중첩

if문은 중첩해서 사용할 수 있다. 대학교 교양 강의를 예를 들어서, 다른 과목과는 다르게 체육과목만 70점 이상이면 Pass고, 미만이면 Non-Pass를 준다고 가정해보자.

var score = 96;
var lecture = "sports";

if("sports" == lecture){
	if (score >= 70){
		console.log("점수 : pass");
	}
	else{
		console.log("점수 : fail");
	}
}else{
	if (score >= 90){
		console.log("점수 : A");
	}else if (score >= 80){
		console.log("점수 : B");
	}else if (score >= 70){
		console.log("점수 : C");
	}else if (score >= 60){
		console.log("점수 : D");
	}else {
		console.log("점수 : F");
	}
}
// 결과는 pass가 나오게 된다.


switch

switch ( 변수 ){
    case A: // 값 A
        // 변수 값이 A 일때 실행할 명령문
        break;
    case B:
        // 변수 값이 B 일때 실행할 명령문
        break;
    case C:
        // 변수 값이 C 일때 실행할 명령문
        break;
    default:
        // 모든 CASE에 부합하지 않을때 실행할 명령문
        break;
}
  • case 뒤에는 해당하는 조건이 오며, 그 아래에 실행할 명령문을 적는다.
  • default는 if...else 조건문에서의 else와 같이 모든 조건에 해당 안 될 때 실행된다.
  • 저렇게 case 각 마지막에 break;를 안 넣어주면 해당하는 case부터 맨 아래까지 모든 case를 수행하게 된다. 그래서 if문처럼 사용하려면 break;를 넣어줘야 한다! (자주 까먹기 쉬운 중요한 내용)
 var num = 3;
switch(num % 2) {
    case 0:
        alert("해당 값은 짝수!");
        break;
    case 1:
        alert("해당 값은 홀수!");
        break;
    default:
        alert("잘못된 입력 값!");
        break;
}


자바스크립트에서 원시 타입과 참조 타입이란 뭔가?

자바스크립트는 동적 타입(dynamic typed) 언어 혹은 느슨한 타입(loosely typed) 언어이며 구문 작성이 자유로운 편이다. 예를 들면 이런 식이다.

const a = 12;
const b = "12";

위와 같이 코드를 작성했을 때, 따로 변수에 자료형을 지정해주지 않았지만 자바스크립트는 자동으로 a는 number 타입으로, b는 string 타입으로 인식한다.
그리고 이런 것도 가능하다.

console.log(a == b); // true
console.log(a === b); // false

첫번째 줄에서는 a와 b를 비교할 때 먼저 형변환을 통해 두 값의 자료형을 일치시킨 뒤 비교하는 동등 연산자(==, equality operator)를 사용했기 때문에 a와 b가 같다는 결과가 나온다.

하지만 두번째 줄에서는 데이터 타입의 변환 없이 비교하여, 타입의 동등 여부까지 검사하는 일치 연산자(===, strict equality operator)를 사용했기 때문에 a와 b가 다르다는 결과가 나온다.

이처럼 Javascript는 다른 언어(C, Java 등)들에 비해 자료형 즉 타입에 비교적 자유롭다고 볼 수 있다. 이런 Javascript의 특성은 코드를 짤 때는 행복하게 느껴질 수도 있지만, 코드에서 오류가 발생했을 땐 지옥의 디버깅을 겪게 하는 악마로 느껴질 수도 있다.

그러므로 자바스크립트 내부에서 변수들의 타입이 어떻게 다루어지는지 이해하고 Typescript를 이용한 정적 타이핑을 한다면 보다 더 슬기로운 코딩생활을 할 수 있지 않겠냐는 생각을 할 수 있다!

JavaScript는 원시 타입과 참조 타입이라는 두가지 자료형을 제공하며 Object를 제외한 모든것들은 Primitive한 성격을 갖고 있다.

Primitive Type : 데이터의 실제 값 할당
Reference Type : 데이터의 위치 값만 할당

1. Primitive Type (원시타입)

원시 타입의 데이터는 변수에 할당이 될 때 메모리 상에 고정된 크기로 저장이 되고 해당 변수가 원시 데이터 값을 보관한다. 원시 타입 자료형은 모두 변수 선언, 초기화, 할당 시 값이 저장된 메모리 영역에 직접적으로 접근한다. 즉, 변수에 새 값이 할당이 될 경우, 변수에 할당된 메모리 블럭에 저장된 값을 바로 변경한다.

1.1 Primitive Type의 종류
Boolean
number
String
null
undefined

1.2 Primitive Type의 변수 복사
각 변수 간에 원시 타입 데이터를 복사할 경우, 데이터의 값이 복사된다.

var x = 100;
var y = x;

x = 99;

console.log(y);	// 100;

데이터의 값을 복사하기 때문에 console을 찍기 전, x를 99로 바뀌었지만 이전의 값인 100을 복사해두었기 때문에 100이 찍히는 것을 볼 수 있다. (이 개념을 잘 이해해야 한다!)

2. Reference Type (참조 타입)

참조 타입의 데이터는 크기가 정해져 있지 않고 변수에 할당이 될 때 값이 직접 해당 변수에 저장될 수 없으며 변수에는 데이터에 대한 참조만 저장된다. 변수의 값이 저장된 힙 메모리의 주소값을 저장한다. 참조 타입은 변수의 값이 저장된 메모리 블럭의 주소를 가지고 있고 자바스크립트 엔진이 변수가 가지고 있는 메모리 주소를 이용해서 변수의 값에 접근한다.

2.1 Referece Type의 종류
Object ( array, function, object )
2.2 Reference Type의 변수 복사
각 변수 간에 참조 타입 데이터를 복사할 경우, 데이터의 참조가 복사된다.

var x = { count : 100 };
var y = x;

x.count = 99;

console.log(y);	// 99

변수 x와 y는 동일한 참조를 담고 있다. 따라서 동일한 객체를 가리키게 된다.
(즉 primitive type과 다르게 reference type은 해당 변수의 값이 저장된 메모리 블록 '주소'를 가져온다는 것이다!)

3. Primitive Type vs Reference Type

3.1 Ex) 1

var list1 = [1, 2, 3];	// 메모리 주소 : 8765e 라고 가정
var list2 = [1, 2, 3];	// 메모리 주소 : 9524d 라고 가정

var isSame = list1 === list2;	8765e === 9524d

console.log(isSame);	// false

list1, list2안의 요소는 같지만 배열을 새롭게 만들어 변수에 담고 있기 때문에 각자 새로운 메모리 위치를 만들어 저장하고 그 위치를 참조하여 변수에 해당 위치값을 저장하는 것과 같다. 따라서 결과는 false가 된다. (배열 안의 내용이 같은데 서로 다르다고 false라고 뜨는 것은 메모리블록 주소가 다르기 때문!!)

3.2 Ex) 2

var list3 = [ 1, 2, 3];
var list4 = list3;

var isSame = list3 === list4;

console.log(isSame);	// true

위의 예제와는 다르게 새롭게 배열을 생성하지않고 list3의 위치값을 그대로 list4에 넣는 것이기 때문에 위치값이 같은 경우라고 할 수 있다. 따라서 결과는 true가 된다.

3.3 Ex) 3

var updateAge = function () {
	this.age++;
};	// '메모리 주소 : 4737d' 라고 가정

var son = {
	age : 3,
    growUp : updateAge
};

var daugther = {
	age : 7,
    growUp : updateAge
};

var mother = {
	age : 38,
    growUp : updateAge,
    children : [ son, daugther ]
};

var father = {
	age : 38,
    growUp : updateAge,
    wife : mother,
    children : [ son, daugther ]
};

// Ex 3.1
if (father.growUp === son.growUp) {	// 4737d === 4737d
	console.log('성장 가능');
}	// 성장 가능

// Ex 3.2
if (father.children === mother.children) {
	console.log('부부!');
}	// false로 console이 찍히지 않음

Ex 3.1의 경우, updateAge라는 변수의 데이터 값으로 함수의 위치값을 저장했다. 가상 메모리 주소가 4737d라는 가정하에 object는 해당 데이터의 위치값을 저장하므로 father.growUp과 son.growUp의 값은 같은 값이 된다. 따라서 결과는 true로 console에 '성장 가능'이 찍히게 된다.

Ex 3.2의 경우, 똑같은 변수들이 담겨있지만 객체 내에서 배열을 새롭게 만들어 mother.children과 father.children은 서로 다른 위치값을 할당을 새롭게 할당받았기 때문에 위치값이 다르게 된다. 따라서 결과는 false로 console에 아무것도 찍히지 않게 된다.



garbage collector (java, c#, javascript)

개발 용어로서 GC 튜닝이라는 용어를 접할 수 있다.
자바스크립트는 눈에 보이지 않는 곳에서 메모리 관리를 수행한다. (콜스택, 메모리 힙 등)
원시값, 객체, 함수 등 우리가 만드는 모든 것은 메모리를 차지한다. 그렇다면 더는 쓸모없어지게 된 것들은 가비지 컬렉터를 통해서 삭제되게 된다.

garbage collection은 쓸모 없어진 객체가 차지하는 메모리를 자동으로 해제하는 것을 의미한다.JavaScript, Python, Java 는 가비지 컬렉터가 자동으로 메모리를 관리해준다.
C, C++ 등 수동으로 메모리 관리하는 언어는 기본적으로 가비지 컬렉터가 없지만, 구현하여 사용할 수 있다.

장점
메모리 관리를 완벽하게 할 필요는 없다.

단점
언제 가비지 컬렉션이 진행될지 예측하기 어렵다.
객체가 쓸모 없어지는 시점에 정확히 메모리가 해제되지 않기 때문에 최적의 메모리 관리가 되지 않는다.
가비지 컬렉터가 동작하는 시간이 든다.
어떤 객체가 쓸모 없는지 판단하는 시간이 소요된다.

자바스크립트 변수와 메모리

가비지 컬렉션 기준

자바스크립트는 도달 가능성(reachability)이라는 개념을 사용해 메모리 관리를 수행한다.
'reachable'한 값은 쉽게 말해 어떻게든 해당 데이터에 접근하거나 사용할 수 있는 값을 의미한다. 도달 가능한 값은 메모리에서 삭제되지 않는다. (직관적으로 생각해보아도, 이렇게 하는 것이 맞다)

예를 들면,
1. 현재 함수의 지역 변수와 매개변수
2. 중첩 함수의 체인에 있는 함수에서 사용되는 변수와 매개변수
3. 전역 변수
4. 기타 등등

이런 값들은 루트라고 부른다. 이 값들은 태생부터 도달 가능하기 때문에, 명백한 이유 없이는 삭제되지 않는다.

또한, 루트가 참조하는 값이나 체이닝으로 루트에서 참조할 수 있는 값은 도달 가능한 값이 된다. 자바스크립트 엔진에서는 가비지 컬렉터가 끊임없이 작동하고 있다! 가비지 컬렉터는 모든 객체를 모니터링하고 도달할 수 없는 객체는 삭제한다.


(admin이 해당 값을 가르키고 있기 때문에 reachable 한 것이고, 지워지지 않는다)

내부 알고리즘

'mark-and-sweep’이라 불리는 가비지 컬렉션 기본 알고리즘은 다음과 같다.

가비지 컬렉터는 루트(root) 정보를 수집하고 이를 ‘mark(기억)’ 한다.
루트가 참조하고 있는 모든 객체를 방문하고 이것들을 ‘mark’ 한다.
mark 된 모든 객체에 방문하고 그 객체들이 참조하는 객체도 mark 한다.
한번 방문한 객체는 전부 mark 하기 때문에 같은 객체를 다시 방문하는 일은 없다!
루트에서 도달 가능한 모든 객체를 방문할 때까지 위 과정을 반복한다.
mark 되지 않은 모든 객체를 메모리에서 삭제하게된다.

0개의 댓글