let a;
메모리 주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름 : a 값: |
var a; // 변수 a 선언
a = 'abc'; // 변수 a에 데이터 할당
var a = 'abc'; // 변수 선언과 할당을 한 문장으로 표현
var의 경우 할당을 한 뒤 선언을 해도 문제가 없지만
let은 before initialization, const는 SyntaxError: Missing initializer in const declaration 오류가 나온다.a = "abc"; var a; console.log(a) // "abc" a = "abc"; let a; // ReferenceError: Cannot access 'a' before initialization a = "abc" const a; // SyntaxError: Missing initializer in const declaration
이렇게 할 이유도 생각도 잘 안하겠지만, 오류가 나는 이유는 var, let, const의 동작 방식이 다르기 때문이다. 자세한 것은 아래의 링크 참고해주세요!
링크 : var, let, const의 차이 - 변수 선언 및 할당
그렇다면 이제 데이터할당의 흐름을 알아보자!
메모리 주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름 : a 값: @5004 |
|||
메모리 주소 | 5002 | 5003 | 5004 | 5005 |
데이터 | "abc" |
데이터 흐름
1. 변수 영역에서 빈 공간(@1003확보한다.
2. 확보한 공간의 식별자를 a로 지정한다.
3. 데이터 영역의 빈 공간(@5004)에 문자열 "abc"를 저장한다.
4. 변수 영역에서 a라는 식별자(@1003)를 검색한다
5. 앞서 저장한 문자열의 주소(@5004)를 @1003의 공간에 대입한다.
var a = 'abc';
a = 'abcdef';
데이터 변환이 이루어 진다면, 데이터 할당은 아래와같이 기존의 데이터를 사용하는게 아닌 새로운 데이터를 만들어서 그 주소값으로 변경시킨다.
메모리 주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름 : a 값: @5004 -> @5005로 변경 |
|||
메모리 주소 | 5002 | 5003 | 5004 | 5005 |
데이터 | "abc" | "abcdef" |
var a = 'abc'; // 변수 a에 "abc"를 할당
a = a + 'def'; // 기존의 "abc"의 값을 "abcdef"로 변경하는 것이 아닌, 아예 새로운 "abcdef" 문자열을 만들어 a에 주소를 저장
var b = 5; // 데이터 영역에 5가 있는지 찾아보고, 없으면 데이터 공간을 만들어 5를 만들어 저장한 뒤 그 주소를 b에 저장
var c = 5; // b에 할당된 데이터 주소값을 그대로 재활용
b = 7; // 기존의 5를 변경하는게 아닌, 7의 값이 있나 찾아보고 없다면 새로 만들어 b에 할당
가비지 컬렉터란?
어떤 데이터에 대해 자신의 주소를 참조하는 변수의 갯수를 ‘참조 카운트' 라고 한다.
(ex. 주소 @5005을 참조하는 주소는 @1003 하나 이므로 참조 카운트는 1)
이때, @1003의 데이터 값을 @5005로 변경하면서 (예: a = "abcdef") 해당 데이터(@5004)의 참조 카운트는 0이 된다.
참조 카운트가 0인 메모리 주소는 가비지 컬렉터의 수거 대상이 된다.
가비지 컬렉터는 런타임 환경에 따라 특정 시점이나 메모리 사용량 포화 상태 시 자동으로 수거한다.
var obj1 = {
a: 1,
b: 'bbb'
}
변수 영역 | 메모리 주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름 : obj1 값: @5001 |
||||
데이터 영역 | 메모리 주소 | 5001 | 5002 | 5003 | 5004 |
데이터 | @7103 ~ ? | 1 | "bbb" | ||
객체 @5001의 변수 영억 |
메모리 주소 | 7103 | 7104 | 7105 | 7106 |
데이터 | 이름 : a 값: @5003 |
이름 : b 값: @5004 |
데이터의 흐름
1. 컴퓨터는 변수 영역의 빈 공간(@1002)를 확보하고, 그 주소 이름을 obj1으로 지정합니다.
2. 임의의 데이터 저장공간 (@5001)에 저장하려고 보니 여러개의 프로퍼티로 이뤄진 데이터 그룹입니다.
그룹 내부의 프로퍼티를 저장하기 위해 별도의 변수 영역 마련 후, 그 영역의 주소(@7103 ~?)를 @5001에 저장
3. @7103 및 @7104에 각각 a와b라는 프로퍼티 이름 지정합니다.
4. 데이터 영역에서 숫자 1 검색. 검색 결과 없으므로 임의로 @5003에 저장하고 이 주소를 @7103에 저장합니다.
'bbb'역시 마찬가지로 똑같이 진행합니다.
var obj1 = {
a: 1,
b: 'bbb',
};
obj1.a = 2;
변수 영역 | 메모리 주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름 : obj1 값: @5001 원래 주소값은 바뀌지 않음 |
||||
데이터 영역 | 메모리 주소 | 5001 | 5002 | 5003 | 5004 |
데이터 | @7103 ~ ? | 2 숫자 2를 검색하고 찾지 못해서 데이터 할당 |
1 | "bbb" | |
객체 @5001의 변수 영억 |
메모리 주소 | 7103 | 7104 | 7105 | 7106 |
데이터 | 이름 : a 값: @5003 -> @5002 변경 |
이름 : b 값: @5004 |
var a = 10;
var b = a;
var obj1 = { c: 10, d: 'ddd' };
var obj2 = obj1;
변수 영역 | 메모리 주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름: a 값: @5001 |
이름: b 값: @5001 |
이름 : obj1 값: @5002 |
이름 : obj2 값: @5002 |
|
데이터 영역 | 메모리 주소 | 5001 | 5002 | 5003 | 5004 |
데이터 | 10 | @7103 ~ ? | "ddd" | ||
객체 @5001의 변수 영억 |
메모리 주소 | 7103 | 7104 | 7105 | 7106 |
데이터 | 이름 : c 값: @5001 |
이름 : b 값: @5003 |
var a = 10;
var b = a;
var obj1 = { c: 10, d: 'ddd' };
var obj2 = obj1;
b = 15;
obj2.c = 15;
위 처럼 변경 한 뒤, 비교를 해보면 아래와 같은 결과가 나온다.
a !== b
obj1 === obj2
기본형 데이터인 a와 b는 다른 주소값을 가지게 되었지만, obj1과 obj2는 여전히 같은 주소값을 가지고 있다. 왜 그럴까?
변수 영역 | 메모리 주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름: a 값: @5001 |
이름: b 값: @5001 => @5004로 변경 |
이름 : obj1 값: @5002 |
이름 : obj2 값: @5002 변경 없음 |
|
데이터 영역 | 메모리 주소 | 5001 | 5002 | 5003 | 5004 |
데이터 | 10 | @7103 ~ ? 변경 없음 | "ddd" | 15 | |
객체 @5001의 변수 영억 |
메모리 주소 | 7103 | 7104 | 7105 | 7106 |
데이터 | 이름 : c 값: @5001 => @5004로 변경 |
이름 : b 값: @5003 |
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
하지만, 굳이 바꾸지 않아도 될 gender까지 바꾸게 되었고, 더 많은 정보가 있다면 비 효율적이게 되버리기 때문에 프로퍼티 갯수와 상관없이 복사하는 함수를 만들 수 있다.
var copyObject = function(target) {
var result = {};
for (var prop in target) {
result[prop] = target[prop];
}
return result;
};
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을 사용
var copyObjectViaJSON = 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 = copyObjectViaJSON(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] } }
var a;
console.log(a); // (1) undefined. 값을 대입하지 않은 변수에 접근
var obj = { a: 1 };
console.log(obj.a); // 1
console.log(obj.b); // (2) 존재하지 않는 프로퍼티에 접근
console.log(b); // c.f) ReferenceError: b is not defined
var func = function() {};
var c = func(); // (3) 반환(return)값이 없으면 undefined를 반환한 것으로 간주.
console.log(c); // undefined
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
참고 : 코어자바스크립트