숫자 number, 문자열 string, 불리언 boolean, null, undefined, 심볼 symbol (ES6)
- 할당이나 연산 시 값이 담긴 주솟값을 바로 복제함.
- 불변성(immutuability)
객체 object, 배열 array, 함수 function, 날짜 date, 정규표현식 RegExp
할당이나 연산 시 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주소값을 복제함.
C/C++ 혹은 JAVA는 2바이트(short), 4바이트(int)
Javascript는 숫자의 경우 8바이트를 확보(정수형과 부동소수형 구분을 하지 않음)
모든 데이터는 바이트 단위의 식별자, 더 정확히는 메모리 주솟값을 통해 서로 구분 및 연결이 가능
변수: 변경 가능한 데이터가 담길 수 있는 공간 또는 그릇
var a;
[1] 변수 선언: 컴퓨터는 메모리에서 비어있는 공간 하나를 확보, 임의로 1003번으로 정함.
이 공간의 이름(식별자)을 a라고 지정.
[2] a에 접근: 컴퓨터는 메모리에서 a라는 이름을 가진 주소를 검색해 해당 공간에 담긴 데이터를 반환
a = 'abc'; // 할당
[1] 데이터 영역의 빈공간(@5004)에 문자열 ‘abc’를 저장
[2] 변수 영역에서 a라는 식별자를 검색(@1003)
[3] 앞서 저장한 문자열의 주소 @5004를 @1003의 공간에 대입
@문자열 ‘abc’의 마지막의 ‘def’를 추가하자
[1] ‘abcdef’라는 문자열을 새로 만들어 별도의 공간(@1003)에 저장
[2] 변수 영역에서 a라는 식별자를 검색(@1003)
[3] 앞서 저장한 문자열의 주소 @5005를 @1003의 공간에 대입
[4]@5004는 (자신의 주소를 저장하는 변수가 하나도 없다면) 가비지 콜렉터가 수거
@500개의 변수를 생성해 모든 변수에 숫자 5를 할당
→ 데이터 영역이 없다면, 주소 공간 2바이트500개 + 데이터 공간 8바이트500개
→ 데이터 영역이 있으므로 중복 참조해 주소 공간 2바이트500개 + 데이터 공간 8바이트1개
→ IF 변수 중 하나를 다른 숫자로 할당?
⇒ 다른 숫자용으로 데이터 영역 하나 차지, 주소 공간에 주소만 대입
→ → 따라서 변수 영역, 데이터 영역을 분리하면 중복된 데이터에 대한 처리 효율도 높아짐.
@기본형 데이터는 모두 불변값.
기본형 데이터 ← 숫자, 문자열, boolean, null, undefined, Symbol
var a = "abc"; // [1]
a = a + "def"; // [1]
var b = 5; // [2]
var c = 5; // [3]
b = 7; // [4]
[1] 변수 a에 문자열 ‘abc’를 할당 뒤에 ‘def’를 추가하면 새로운 ‘abcdef’를 만들어 그 주소를 변수 a에 저장
[2] 데이터 영역에서 5를 찾고, 없으면 그제서야 데이터 공간을 하나 만들어 저장, 해당 데이터 공간의 주소를 b에 저장
[3] 이미 만들어둔 데이터 공간의 주소를 c에 저장
[4] 기존에 저장해둔 7의 데이터 공간의 주소를 찾아서 있으면 재활용, 없으면 새로 만들어서 b에 저장.
var obj1 = {
a: 1,
b: 'bbb'
};
[1] 변수 영역 빈 공간 @1002를 확보, 그 주소의 이름을 obj로 지정
[2] 데이터 → 여러 개의 프로퍼티로 이뤄진 데이터 그룹
해당 그룹 내의 프로퍼티들을 저장 위해 별도의 변수 영역을 마련, 그 영역의 주소를 @5001에 저장.
[3] @7103 및 @7104에 각각 a와 b라는 프로퍼티 이름을 지정
[4] 데이터 영역에서 숫자 1 검색, 검색 결과가 없으므로 임의로 @5003에 저장, 이 주소를 @7103에 저장. 문자열 ‘bbb’ 역시 임의로 @5004에 저장, 이 주소를 @7104에 저장
@참조형 데이터의 프로퍼티 재할당
var obj1 = {
a: 1,
b: 'bbb',
};
obj1.a = 2;
[1] 데이터 영역에서 숫자 2 검색
[2] 검색 결과가 없음, 빈 공간인 @5005에 저장, 이 주소를 @7103에 저장
[3] 이 주소를 @7103에 저장. @7104에 이름 arr를 지정
→ 즉 새로운 객체가 아닌 기존의 객체 내부의 값만 바뀐 것!
@중첩된 참조현 데이터(객체)의 프로퍼티 할당
@1002 → @5001 → (@7103 ~ ?) → @7104 → @5003 → (@8104 ~ ?) → @8105 → @5004 → 4 반환
@여기서 재할당 명령을 아래와 같이 내리면?
obj.arr = 'str';
@5003은 더 이상 자신의 주소를 참조하는 변수가 하나도 없게 됨
→ GC 수거 대상이 됨.
기본형과 참조형의 복사 과정은 동일, 데이터 할당 과정에서 이미 차이 발생, 변수 복사 이후의 동작에도 큰 차이 발생
var a = 10;
var b = a;
var obj1 = {c:10, d:'ddd'};
var obj2 = 'obj1';
@기본형 데이터
@1002 확보, 식별자=b로 지정
식별자 a를 검색해서 @1001에 저장된 값인 @5001을 들고 좀 전에 확보해둔 @1002에 값으로 대입
@참조형 데이터
@1004 확보, 식별자=obj2로 지정
식별자 obj1을 검색해(@1003) 그 값인 @5002를 들고 @1004에 값으로 대입
@변수 복사 이후 값 변경 결과 비교 - 객체의 프로퍼티 변경 시
var a = 10;
var b = a;
var obj1 = {c:10, d:'ddd'};
var obj2 = obj1;
b = 15;
obj2.c = 20;
a !== b
obj1 === obj2
@변수 복사 이후 값 변경 결과 비교 (2) - 객체 자체를 변경했을 때
var a = 10;
var b = a;
var obj1 = { c:10, d:"ddd"};
var obj2 = obj1;
b = 15;
obj2 = { c:20, d:"ddd"};
불변 객체 immutable object
@불변 객체가 필요한 경우
아래 예시에서 정보가 바뀐 시점에 알림을 보내야 함
바뀌기 전의 정보와 후의 정보의 차이를 가시적으로 보여줘야 하는 등의 기능을 구현해야 함
→ 변경 전, 후에 서로 다른 객체를 바라보게 만들어야 함.
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
→ 보다 효율적으로 바꾼 코드
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
모두가 규칙을 따르지 않고는 프로퍼티 변경을 할 수 없게끔 시스템적으로 제약을 거는 편이 안전
→ 실제: immutable.js, baobab.js 등의 라이브러리 활용
@객체의 깊은 복사를 수행하는 범용 함수
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;
};
@다른 방법
[1] 값을 대입하지 않은 변수, 즉 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근할 때
[2] 객체 내부의 존재하지 않는 프로퍼티에 접근하려고 할 때
[3] return 문이 없거나 호출되지 않는 함수의 실행 결과
// 자동으로 undefined를 부여하는 경우
var a;
console.log(a); // undefined, 1 - 값 대입하지 않은 변수
var obj = {a:1};
console.log(obj.a); // 1
console.log(obj.b); // undefined, 2 - 존재하지 않는 프로퍼티에 접근
console.log(b); // c.f) Reference error - b is not defined
var func = function(){};
var c = func(); // undefined, 3 - return 값이 없으면 undefined를 반환한 것으로 간주
console.log(c); // undefined
// undefined와 배열
var arr1 = [];
arr1.length = 3;
console.log(arr1); // [empty * 3]
var arr2 = new Array(3);
console.log(arr2); // [empty * 3]
var arr3 = [undefined, undefined, undefined];
console.log(arr3); // [undefined * 3]
// 빈 요소와 배열의 순회
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, i){return !v;}); // [undefined]
arr2.filter(function(v, i){return !v;}); // []
arr1.reduce(function(p, c, i){return p + c + i;}); // undefined011
arr2.reduce(function(p, c, i){return p + c + i;}); // 11
arr1은 배열의 모든 요소를 순회해 결과를 출력
arr2은 각 메서드들이 비어 있는 요소에 대해서는 어떠한 처리도 하지 않고 건너뜀
@배열도 객체
→ 존재하지 않는 프로퍼티에 대해서는 순회할 수 없음
→ 객체와 마찬가지로 특정 인덱스에 값을 지정할 때 비로소 빈 공간을 확보, 인덱스를 이름으로 지정하고 데이터의 주솟값을 저장하는 등의 동작을 함.
→ 사용자가 undefined를 명시적으로 부여한 경우 vs 비어있는 요소에 접근할 때 반환되는 경우 차이를 알 수 있음.
// undefined vs null 비교
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
1 == true => true
true == true => true
1 === true => false
true === true => true