| 데이터 타입 | 타입 | 하위분류 | 차이 | |
|---|---|---|---|---|
| 기본형(원시형) primitive type | 불변성 immutability | 숫자 number 문자열 string 불리언 boolean null undefined 심볼 Symbol | 깂이 담긴 주솟값 바로 복제 | |
| 참조형 reference type | 객체 object | 배열 Array 함수 Function 날짜 Date 정규표현식 RegExp Map, WeakMap Set, WeakSet | 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제 |
메모리 영역에서 자바스크립트 데이터가 처리되는 과정, 식별자, 변수

비트 bit : 0 또는 1만 표현할 수 있는 하나의 메모리 조각
바이트 byte : 검색 시간을 줄이고 표현할 수 있는 값이 늘어난 공간
모든 데이터는...
동적 타입 언어 (JavaScript, Python, PHP)
정적 타입 언어 (TypeScript, Java, C++)
var a;
< 변수 선언에 대한 메모리 영역의 변화 >

var a; // 변수 a 선언
a = 'abc'; // 변수 a에 데이터 할당
var a = 'abc'; // 변수 선언과 할당
< 데이터 할당에 대한 메모리 영역의 변화 >


변수 variable : 변수 영역 메모리 변경 가능성 있음
상수 constant : 변수 영역 메모리 변경 가능성 없음
기본형 데이터(숫자, 문자열, boolean, null, undefined, Symbol)
불변값 성질 : 변경은 새로 만드는 동작을 통해서만 이뤄짐
불변성 예시 1
var a = 'abc';
a = a + 'def';
불변성 예시 2
var b = 5;
var c = 5;
b = 7;
참조형 데이터
가변값 : 참조형 데이터 내부의 프로퍼티를 변경할 때만
불변값
기본형 데이터와의 차이
참조형 데이터(객체)의 할당
var obj1 = {
a: 1,
b: 'bbb',
};

var obj1 = {
a: 1,
b: 'bbb',
};
obj1.a = 2;중첩된 참조형 데이터(중첩 객체)의 프로퍼티 할당
var obj = {
x: 3,
arr: [ 3, 4, 5 ]
};

obj.arr[1] 검색
@1002 -> @5001 -> (@7103 ~ ?) -> @710 4 -> @5003 -> (@8104 ~ ?) -> @8105 -> @5004 -> 4 반환
obj 검색 1: obj라는 식별자를 가진 주소 찾기 (@1002)
obj 검색 2: 값이 주소이므로 그 주소로 이동 (@5001)
obj 검색 3: 값이 주소이므로 그 주소로 이동 (@7103 ~ ?)
obj.arr 검색 1: arr이라는 식별자를 가진 주소 찾기 (@7104)
obj.arr 검색 2: 값이 주소이므로 그 주소로 이동 (@5003)
obj.arr 검색 3: 값이 주소이므로 그 주소로 이동 (@8104 ~ ?)
obj.arr[1] 검색 1: 인덱스 1에 해당하는 주소 찾기 (@8105)
obj.arr[1] 검색 2: 값이 주소이므로 그 주소로 이동 (@5004)
obj.arr[1] 검색 3: 값이 숫자형 데이터이므로 4 반환
obj.arr='str'; 재할당
@5006에 문자열 'str' 저장, 그 주소 @7104에 저장
@5003은 더이상 자신의 주소를 참조하는 변수 없음
@5003의 참조 카운트는 @7104에 @5003이 저장돼 있던 시점까지는 1이었다가 @7104에 @5006이 저장되는 순간 0이 됨
참조 카운트가 0인 메모리 주소는 가비지 컬렉터 GC 의 수거 대상 됨
수거된 메모리는 다시 새로운 값을 할당할 수 있는 빈 공간이 됨
@5003에 담겨있던 @8104 ~ ? 값도 사라지므로 연쇄적으로 각 데이터들의 참조 카운트가 0이 되고 GC 수거 대상이 되어 함께 사라짐
변수 복사
var a = 10;
var b = a;var obj1 = { c: 10, d: 'ddd' };
var obj2 = obj1; 동작 과정은 같음
var a = 10;
var b = a;
var obj1 = { c: 10, d: 'ddd' };
var obj2 = obj1;
b = 15;
obj2.c = 20;

변수 a와 b는 서로 다른 주소,
변수 obj1와 obj2는 같은 객체 바라봄
=> 참조형 데이터 내부의 프로퍼티를 변경할 때만 가변값
a !== b
obj1 === obj2
모든 데이터 타입은 참조형 데이터일 수 밖에 없지만
기본형은 주솟값을 복사하는 과정이 한 번만 이뤄지고
참조형은 한 단계를 더 거침
var a = 10;
var b = a;
var obj1 = { c: 10, d: 'ddd' };
var obj2 = obj1;
b = 15;
obj2 = { c: 20, d: 'ddd' };

불변 객체가 필요한 상황
: 값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 경우
객체의 가변성에 따른 문제점
var user = {
name: 'Jaenam',
gender: 'male'
};
var changeName = function (user, newName) {
var newUser = user;
newUser.name = newName;
return newUser;
};
var user2 = changeName(user, 'Jung');
if (user !== user2) {
console.log('유저 정보가 변경되었습니다.');
}
console.log(user.name, user2.name); // Jung Jung
console.log(user === user2); // true
객체의 가변성에 따른 문제점의 해결 방법
var user = {
name: 'Jaenam',
gender: 'male'
};
var changeName = function (user, newName) {
return {
name: newName,
gender: user.gender
};
};
var user2 = changeName(user, 'Jung');
if (user !== user2) {
console.log('유저 정보가 변경되었습니다.'); // 유저 정보가 변경되었습니다.
}
console.log(user.name, user2.name); // Jaenam Jung
console.log(user === user2); // false
기존 정보를 복사해서 새로운 객체를 반환하는 함수 (얕은 복사)
user 불변 객체
var copyObject = function (target) {
var result = {};
for (var prop in target) {
result[prop] = target[prop];
}
return result;
};
var user = {
name: 'Jaenam',
gender: 'male'
};
var user2 = copyObject(user);
user2.name = 'Jung';
if (user !== user2) {
console.log('유저 정보가 변경되었습니다.'); // 유저 정보가 변경되었습니다.
}
console.log(user.name, user2.name); // Jaenam Jung
console.log(user === user2); // false
얕은 복사와 깊은 복사
얕은 복사 shallow copy : 바로 아래 단계의 값만 복사
깊은 복사 deep copy : 내부의 모든 값들을 하나하나 찾아서 전부 복사
중첩된 객체에 대한 얕은 복사
var user = {
name: 'Jaename',
urls: {
portfolio: 'http://github.com/abc',
blog: 'http://blog.com',
facebook: 'http://facebook.com/abc'
}
};
var user2 = copyObject(user);
user2.name = 'Jung';
console.log(user.name === user2.name); // false
user.urls.porfolio = 'http://portfolio.com';
console.log(user.urls.portfolio === user2.urls.portfolio); // true
user.urls.blog = '';
console.log(user.urls.blog === user2.urls.blog); // true
중첩된 객체에 대한 깊은 복사
var user2 = copyObject(user);
user2.urls = copyObject(user.urls);
user.urls.portfolio = 'http://portfolio.com';
console.log(user.urls.portfolio === user2.urls.portfolio); // false
user2.urls.blog = '';
console.log(user.urls.blog === user2.urls.blog); // false
객체의 깊은 복사를 수행하는 범용 함수
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;
obj.b.d[1] =3;
console.log(obj); // { a: 1, b: { c: null, d: [1, 3] } }
console.log(obj2); // { a: 3, b: { c: 4, d: {0:1, 1:2} } }
JSON을 활용한 간단한 깊은 복사
__proto__, getter/setter 같은 JSON으로 변경할 수 없는 프로퍼티 무시함var copyObjectVialJSON = function (target) {
return JSON.parse(JSON.stringify(target));
};
var obj = {
a: 1,
b: {
c: null,
d: [1, 2],
func1: function () { console.log(3); }
},
func2: function () { console.log(4); }
};
var obj2 = copyObjectVialJSON(obj);
obj2.a = 3;
obj2.b.c = 4;
obj.b.d[1] = 3;
console.log(obj); // { a: 1, b: { c: null, d: [1, 3], func1: f() }, func2: f() }
console.log(obj2); // { a: 3, b: { c: 4, d: [1, 2] } }
undefined 값이 없음
사용자가 명시적으로 지정할 수는 있음 (null이 있으니 굳이?)
값이 존재하지 않을 때 자바스크립트 엔진이 자동으로 부여
값을 대입하지 않은 변수 === 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근할 때
자바스크립트가 직접 undefined 할당
var 변수는 LE가 활성화될 때 생성되면서 동시에 undefined로 초기화됨
let, const 변수는 LE가 활성화 될 때 생성되지만 undefined를 할당하지 않은 채 초기화됨
let, const 변수 -> 특정 값 할당 전까지 접근 불가
var a;
console.log(a); // undefined
값을 대입하지 않은 배열의 경우
값을 지정하지 않은 인덱스 === 아직 존재하지 않는 프로퍼티
var arr1 = [];
arr1.length = 3;
console.log(arr1); // [empty x 3] 3개의 빈 요소 확보했지만 undefined조차 할당되지 않음
var arr2 = new Array(3);
console.log(arr2); // [empty x 3] 비어있는 요소 3개
var arr3 = [undefined, undefined, undefined];
console.log(arr3); // [undefined, undefined, undefined]
빈 요소와 배열의 순회
unedfined 그 자체로 값 === 고유의 키 값(프로퍼티 이름) 실존 => 순환의 대상
아무것도 하지 않고 접근 === 고유의 키 값(프로퍼티) 존재 X
var arr1 = [undefined, 1];
var arr2 = [];
arr2[1] = 1;
arr1.forEach(function (v, i) { console.log(v, i); }); // undefined 0 / 1 1
arr2.forEach(function (v, i) { console.log(v, i); }); // 1 1
arr1.map(function (v, i) { return(v, i); }); // [NaN, 2]
arr2.map(function (v, i) { return(v, i); }); // [empty, 2]
arr1.filter(function (v) { return !v; }); // [undefined]
arr2.filter(function (v) { return !v; }); // []
arr1.reduce(function (p, c, i) { return p + c + i; }, ''); // undefined011
arr2.reduce(function (p, c, i) { return p + c + i; }, ''); // 11
객체 내부의 존재하지 않는 프로퍼티에 접근하려고 할 때
var obj = { a: 1 };
console.log(obj.a); // 1
console.log(obj.b); // undefined
console.log(b); // 선언되지 않은 변수 접근 c.f) ReferenceError: b is not defined
return 문이 없거나 호출되지 않는 함수의 실행 결과
var func = function() { };
var c = func(); // 반환 return 값이 없으면 undefined 반환
console.log(c); // undefined
null 비어있음
비어있음을 명시적으로 나타내고 싶을 때
주의! typeof null이 object라는 자바스크립트 자체 버그
변수의 값이 null인지 판별 위해 일치 연산자 identity operator
var n = null;
console.log(typeof n); // object
console.log(n == undefined); // true 동등 연산자
console.log(n == null); // true
console.log(n === undefined); // false 일치 연산자
console.log(n === null); // true