
값의 저장 방식 과, 불변성 여부 이다.1-1. 기본형 : 값이 담긴 주소값을 바로 복제
1-2. 참조형 : 값이 담긴 주소값들로 이루어진 묶음을 가리키는 주소값을 복제
2-1. 기본형 : 불변성을 띔
2-2. 참조형 : 불변성을 띄지 않음
메모리를 기준으로 다시한번 생각해보는 두 가지 주요 개념
1-1. 변수 : 변수 영역 메모리를 변경할 수 있음
1-2. 상수 : 변수 영역 메모리를 변경할 수 없음
2-1. 불변하다 : 데이터 영역 메모리를 변경할 수 없음
2-2. 불변하지 않다 : 데이터 영역 메모리를 변경할 수 있음
객체의 변수(프로퍼티) 영역의 별도 존재 여부 이다./** 선언과 할당을 풀어 쓴 방식 */
var str;
str = 'test!';
/** 선언과 할당을 붙여 쓴 방식 */
var str = 'test!';


1-1. 만약 이미 입력한 문자열이 길어진다면?
숫자는 항상 8byte로 고정이지만, 문자는 고정이 아니다.(영문 : 1byte, 한글 : 2byte)
이때, 1003 주소에 할당된 데이터를 변환하려 할 때 훨씬 더 큰 데이터를 저장하려 한다면 → 1004 이후부터 저장되어있는 모든 데이터를 오른쪽으로 전부 미뤄야 하기 때문이다.
별도 공간에 주소를 확보한 후에 주소를 가져오는것이 더 효율적이다.
2-1. 똑같은 데이터를 여러번 저장해야 한다면?

a. 바로 대입하는 case) 숫자형은 8 바이트 고정일 경우
1만개 * 8byte = 8만 byte
b. 변수 영역에 저장되는 데이터는 2바이트로 가정하고 변수 영역에 별도 저장 한 경우
1만개 * 2byte = 2만 byte + 데이터 영역 8byte = 총 2만 8바이트
변수 vs 상수
1-a. 변수 : 변수 영역 메모리를 변경할 수 있음
1-b. 상수 : 변수 영역 메모리를 변경할 수 없음
불변하다 vs 불변하지 않다
2-a. 불변하다 : 데이터 영역 메모리를 변경할 수 없음
데이터 영역의 주소의 값은 그대로이기 때문
2-b. 불변하지 않다 : 데이터 영역 메모리를 변경할 수 있음
// a라는 변수가 abc에서 abcdef가 되는 과정을 통해 불변성을 유추해보자!
// 'abc'라는 값이 데이터영역의 @5002라는 주소에 들어갔다고 가정할게요.
var a = 'abc';
// 'a'라는 값은 변수영역
'abc' 는 데이터 영역
a = a + 'def';
// 'def'라는 값이 'abc'의 데이터 영역 붙은것은 아니고 'abc'는 그대로 있고 'abcdef'라는 새로운 값이 생긴다.
//'abcdef'라는 새로운 값이 생기면서 이주소가 변수영역으로 갈아 껴진것이다. @5002 -> @5003
// 그렇기 때문에 변수이면서, 불변하다.
// 이 때, @5002는 더 이상 사용되지 않기 때문에 가비지컬렉터의 수거 대상이 된다.
// 참조형 데이터는 별도 저장공간(obj1을 위한 별도 공간)이 필요하다!
var obj1 = {
a: 1,
b: 'bbb,
};

1. 변수 영역의 빈공간(1001번이 빈공간이다.)을 찾는다.
2. 'obj1'은 참조형 데이터이기 때문에 별도공간을 따로 마련했다.
3. a=7103, b=7104 로 세팅했다.
4. a는 1이라는 데이터가 필요하기 때문에 데이터 영역에 1=5001 값을 세팅해주었다.
5. 그후 a는 1의 주소값 @5001을 가지고 온다.
6. b는 'bbb'의 데이터가 필요하기 때문에 데이터 영역에 'bbb'=5002 값을 세팅해주었다.
7. 그후 b는 'bbb'의 주소값 @500를 가지고 온다.
8. 비로소 @7103 ~ @7104 까지의 주소를 'obj1'에 세팅한다.
기본형 데이터의 변수 할당 과정과 차이점은 객체의 변수(프로퍼티) 영역의 별도 존재 여부이다.
참조형 데이터가 불변하지 않다(가변하다)라고 하는 이유
var obj1 = {
a: 1,
b: 'bbb',
};
// 데이터를 변경해자!
obj1.a = 2;

불변하지 않다(=가변하다)라고 한다.var obj = {
x: 3,
arr: [3, 4, 5],
}
// obj.arr[1]

'obj'를 위한 별도 공간에 'x', 'arr' 을 세팅한다.
'x'를 먼저 할당하기 위해서 데이터 영역에 3이라는 값을 추가한다.
그후 'x'는 3의 주소값 @5001을 가져온다.
'arr'도 참조형 데이터 이기 때문에 'arr'의 별도 공간을 마련해 준다.
'arr'의 별도공간에 3,4,5 값을 세팅하기 위해 데이터 베이스에 4,5 값을 추가한다.
그후 'arr'의 별도공간에 0번째에는 @5001, 1번째에는 @5002, 3번째에는 @5003 주소값을 가져온다.
7.'obj'별도공간의 'arr'가 @8104 주소를 가져온다.
비로소 변수영역의 'objrk @7103 주소를 가져오면서 완성된다.
참조카운트란?
객체를 참조하는 변수나 다른 객체의 수를 나타내는 값이다.
가비지컬렉터(GC, Garbage Collector)란?
더 이상 사용되지 않는 객체를 자동으로 메모리에서 제거하는 역할을 한다.
// STEP01. 선언을 먼저 한후.
var a = 10; //기본형
var obj1 = { c: 10, d: 'ddd' }; //참조형
// STEP02. 복사를 수행하자!.
var b = a; //기본형
var obj2 = obj1; //참조형

// STEP03. 복사 이후 값을 변경해 보자!
b = 15;
obj2.c = 20;

영향 없음변경이 됨
// 기본형 변수 복사의 결과는 다른 값!
a !== b;
// 참조형 변수 복사의 결과는 같은 값이 나온다.
obj1 === obj2;
b = 15;
obj2 = { c: 20, d: 'ddd'};

obj2 변수는 참조형 데이터이고, 참조형 데이터의 값을 변경한 것임에도 불고하고 이전 케이스와는 다르게 obj1과는 바라보는 데이터 메모리 영역의 값이 달라졌다!
참조형 데이터가 ‘가변값’이라고 할 때의 ‘가변’은 참조형 데이터 자체를 변경할 경우가 아니라, 그 내부의 프로퍼티를 변경할 때 성립한다고 할 수 있겠다.
앞선 과정에서, 가변하다 와 불변하다 의 개념을 배웠다.
가변이 성립한다.불변하다라고 볼 수 있다.가변성 문제점 예시)
// user 객체를 생성
var user = {
name: 'wonjang',
gender: 'male',
};
// 이름을 변경하는 함수, 'changeName'을 정의
// 입력값 : 변경대상 user 객체, 변경하고자 하는 이름
// 출력값 : 새로운 user 객체
// 특징 : 객체의 프로퍼티(속성)에 접근해서 이름을 변경했다! -> 가변
var changeName = function (user, newName) {
var newUser = user;
newUser.name = newName;
return newUser;
};
// 변경한 user정보를 user2 변수에 할당한다.
// 가변이기 때문에 user1도 영향을 받게 된다.
var user2 = changeName(user, 'twojang');
// 결국 아래 로직은 skip하게 된다.
if (user !== user2) {
console.log('유저 정보가 변경되었다.');
}
console.log(user.name, user2.name); // twojang twojang
console.log(user === user2); // true
위 예시의 문제점 개선)
// user 객체를 생성
var user = {
name: 'wonjang',
gender: 'male',
};
// 이름을 변경하는 함수 정의
// 입력값 : 변경대상 user 객체, 변경하고자 하는 이름
// 출력값 : 새로운 user 객체
// 특징 : 객체의 프로퍼티에 접근하는 것이 아니라, 아에 새로운 객체를 반환 -> 불변
var changeName = function (user, newName) {
return {
name: newName,
gender: user.gender,
};
};
// 변경한 user정보를 user2 변수에 할당한다.
// 불변이기 때문에 user1은 영향이 없다!
var user2 = changeName(user, 'twojang');
// 결국 아래 로직이 수행 된다.
if (user !== user2) {
console.log('유저 정보가 변경되었다.');
}
console.log(user.name, user2.name); // wonjang twojang
console.log(user === user2); // false
changeName 함수는 새로운 객체를 만들기 위해 변경할 필요가 없는 gender 프로퍼티를 하드코딩으로 입력했다. ⇒ 만일 이러한 속성이 10개 이거나, 그이상 이라면?
이러한 문제점을 개선하기 위해 얕은복사 방법을 사용한다.
//이런 패턴은 어떨까요?
var copyObject = function (target) {
var result = {};
// for ~ in 구문을 이용하여, 객체의 모든 프로퍼티에 접근할 수 있다.
// 하드코딩을 하지 않아도 된다.
// 이 copyObject로 복사를 한 다음, 복사를 완료한 객체의 프로퍼티를 변경한다.
for (var prop in target) {
result[prop] = target[prop];
}
return result;
}
var user = {
name: 'wonjang',
gender: 'male',
};
var user2 = copyObject(user);
user2.name = 'twojang';
if (user !== user2) {
console.log('유저 정보가 변경되었다.');
}
console.log(user.name, user2.name);
console.log(user === user2);
얕은 복사는 바로 아래 단계의 값만 복사 하기 때문에, 중첩된 객체의 경우 참조형 데이터가 저장된 프로퍼티를 복사할 때, 주소값만 복사 한다.깊은 복사는 내부의 모든 값들을 하나하나 다 찾아서 모두 복사하는 방법 이다.중첩된 객체에 대한 얕은 복사 예시)
var user = {
name: 'wonjang',
urls: {
portfolio: 'http://github.com/abc',
blog: 'http://blog.com',
facebook: 'http://facebook.com/abc',
}
};
var user2 = copyObject(user);
user2.name = 'twojang';
// 바로 아래 단계에 대해서는 불변성을 유지하기 때문에 값이 달라지죠.
console.log(user.name === user2.name); // false
// 더 깊은 단계에 대해서는 불변성을 유지하지 못하기 때문에 값이 같아요.
// 더 혼란스러워 지는거죠 ㅠㅠ
user.urls.portfolio = 'http://portfolio.com';
console.log(user.urls.portfolio === user2.urls.portfolio); // true
// 아래 예도 똑같아요.
user2.urls.blog = '';
console.log(user.urls.blog === user2.urls.blog); // true
ser.urls 프로퍼티도 불변 객체로 만들어야 한다.중첩된 객체에 대한 깊은 복사 예시)
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);
결론 객체의 프로퍼티 중, 기본형 데이터는 그대로 복사 + 참조형 데이터는 다시 그 내부의 프로퍼티를 복사한다! ⇒ 재귀적 수행!(recursive)a. 재귀적 수행: 함수나 알고리즘이 자기 자신을 호출하여 반복적으로 실행되는 것을 말한다.
결론을 적용한 예시 코드)
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);
1-1. 변수에 값이 지정되지 않은 경우, 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근할 때
1-2. .이나 []로 접근하려 할 때, 해당 데이터가 존재하지 않는 경우 일때
1-3. return 문이 없거나 호출되지 않는 함수의 실행 결과 일때
var a;
console.log(a); // (1) 값을 대입하지 않은 변수에 접근
var obj = { a: 1 };
console.log(obj.a); // 1
console.log(obj.b); // (2) 존재하지 않는 property에 접근
// console.log(b); // 오류 발생
var func = function() { };
var c = func(); // (3) 반환 값이 없는 function
console.log(c); // undefined
2-1. 지금 undefined로 나오는 이 변수가, 필요에 의해 할당한건지 자바스크립트 엔진이 반환한건지 구분할 수가 없다.
2-2. 없다를 명시적으로 표현할 때는 undefined를 사용하지 말자!
typeof null( javascript 자체 버그 이다.)null 예시)
var n = null;
console.log(typeof n); // object
//동등연산자(equality operator)
//타입까지 일치하지 않아도 된다.
console.log(n == undefined); // true
console.log(n == null); // true
//일치연산자(identity operator)
//undefined와 null의 구분을 확실하게 할수있다.
console.log(n === undefined); // false
console.log(n === null); // true