let a;
a = 'abc';
a라는 이름을 가진 주소를 검색해서 'abc'를 직접 할당하는 것이 아니라 데이터를 저장하기 위한 별도의 메모리 공간을 다시 확보해서 문자열 'abc'를 저장하고, 그 주소를 변수영역에 저장한다.
데이터를 저장할 때 숫자의 경우 메모리 공간을 8바이트(64비트)만큼 확보한다. 문자열의 경우에는 특별히 정해진 규격이 없다. 만약 미리 확보한 공간 내에서만 데이터를 변환할 수 있다면 변환한 데이터를 다시 저장하기 위해 변환된 데이터의 크기에 맞는 공간을 늘리는 작업을 해야할 것이다. 메모리의 중간에 있는 데이터를 늘려야 한다면 해당 공간보다 뒤에 있는 데이터를 뒤로 옮기고 이동시킨 주소를 각 식별자에 다시 연결하는 작업을 해야한다. 따라서 효율적으로 데이터를 변환하기 위해서는 변수와 데이터를 별도의 공간에 나누어 저장하는 것이 좋다.
불변성(immutability) 여부를 구분할 때의 변경 가능성의 대상은 데이터 영역
메모리 이다. 변수를 할당할 때 기존의 데이터에 입력한 값이 있는지 확인해서 있다면 그 데이터의 주소를 사용한다. 변수를 재할당하면 기존의 데이터를 변화하는 것이 아니라 재할당 값을 검색해서 없다면 새로운 주소로 저장한다. 이미 입력된 데이터는 가비지 컬렉팅을 당하지 않는 한 변하지 않는다. 데이터의 주소를 저장하는 변수가 하나도 없게 되면 그 데이터는 가비지 컬렉터에 의해서 수거된다.
let obj = {
a: 1,
b: 'bb'
};
빈공간을 확보해 그 주소의 이름을 obj로 지정한다. obj의 변수 영역에는 데이터를 저장하는 공간의 주소(@5001)를 저장한다. 별도의 변수 영역(a와b를 저장하는 공간)을 확보해 이 영역의 주소(@7103 @7104)를 obj의 데이터 영역(@5001)에 저장한다. 별도의 변수 영역에서는 a와 b라는 이름을 가진 변수 영역에 값(1, 'bb')을 저장하는 주소(@5003 @5004)를 저장한다.
데이터 할당시 기본형, 참조형 모두 주솟값을 참조한다. 기본형은 주솟값을 복사하는 과정이 한 번만 이루어지고 참조형은 한 단계 이상 이루어진다는 차이가 있다.
가변값
이라고 한다. 데이터 자체를 변경하는 것이 아닌 그 내부의 프로퍼티를 변경할 때 가변값이라고 말할 수 있다.객체의 프로퍼티 변경 시
let obj1 = { a : 10, b : "bb" } let obj2 = obj1; obj2.a = 20;
obj1 === obj2;
객체 자체를 변경했을 때
let obj1 = { a : 10, b : "bb" } let obj2 = obj1; obj2 = { a : 20, b : "bb" }
obj1 !== obj2;
참조형 데이터는 데이터 자체를 변경하면(새로운 데이터를 할당하면) 기본형 데이터처럼 불변성
을 가질 수 있다.
- 새로운 객체를 만들어 재할당
- 자동으로 새로운 객체를 만드는 도구 활용(immutable.js, immer.js, immutability-helper 라이브러리, spread operator, Object.assign 메서드 활용)
얕은 복사는 바로 아래 단계의 값만 복사하는 방법, 깊은 복사는 내부의 모든 값을 하나하나 찾아서 전부 복사하는 방법이다.
프로퍼티를 복사할 때 주솟값만 복사하므로 중첩된 객체에서 얕은 복사를 할 경우, 원본과 사본이 모두 동일한 데이터 주소를 가리킨다. 사본을 바꾸면 원본도 바뀌고, 원본을 바꾸면 사본도 바뀌게된다.
let student = {
name: 'jade',
grade: {
math: 'A',
english: 'B',
music: 'D'
}
}
위의 객체를 copyObject를 만들어서 객체 복사를 하게되면 다음과 같다.
let copyObject = function(target){ let result = {} for (var prop in target){ result[prop] = target[prop]; } return result; }
let student2 = copyObject(student); student2.name = 'amy';
student.name !== student2.name; student !== student2;
student.name
을 바꿀 경우, 기존의 student 객체와 변경된 객체는 같지 않다.
let student2 = copyObject(student); student2.grade.math = 'B';
student.grade.math === student2.grade.math; student === student2;
student.grade
의 프로퍼티를 바꾸는 경우에는 기존의 student.grade.프로퍼티 값과 변경된 값은 같아지게 된다.
grade
의 프로퍼티는 복사가 이루어지지 않았기때문에 기존 데이터를 그대로 참조한다. 이를 얕은 복사
라고한다.
객체의 프로퍼티가 기본형일 때는 그대로 복사하면 되지만 참조형 데이터일 때는 그 내부의 프로퍼티들을 다시 복사해야 한다.
깊은 복사
를 하기 위해서 참조형 데이터가 있을 때마다 재귀적으로 수행해야한다. 객체 내에 존재하는 모든 참조형 데이터를 복사한다.
let copyObjectDeep = function(target){
let result = {};
// null의 type도 object를 반환한다.
if(typeof target === 'object' && target !== null){
for (var prop in target){
result[prop] = copyObjectDeep(target[prop]);
}
}else {
result = target;
}
return result;
}
깊은 복사하는 간단한 방법
let copyObjectDeep = function(target){
return JSON.parse(JSON.stringify(target));
}
ajax 통신을 통해 받은 데이터를 저장한 객체를 복사할 때와 같이 순수한 정보만을 다룰 때 활용하기 좋은 방법이다.
자바스크립트 자체에서 undefined를 반환하는 경우는 다음 세 가지와 같다.
undefined를 직접 할당하여 사용하는 경우도 있다. 빈배열과 undefined를 사용했을 때의 차이는 다음과 같다.
빈배열
let arr1 = []; arr1.length = 2; console.log(arr1); arr1.filter(function(v) {return !v;});
[empty * 2] []
undefined 할당
let arr2 = [undefined, 1]; console.log(arr2); arr2.filter(function(v) {return !v;});
[undefined, 1] [undefined]
undefined를 직접 할당하게 되면 forEach, map, filter, reduce와 같은 배열 메서드를 사용했을 때 배열의 모든 요소를 순회해서 결과를 출력한다. 하지만 비어있는 요소에 대해서는 어떠한 처리도 하지않고 건너뛴다. 이처럼 할당된 undefined는 실존하는 데이터지만 자바스크립트 자체에서 반환된 undefined는 문자 그대로 값이 없음을 나타낸다.
같은 의미의 null은 비어있음을 명시적으로 나타내고 싶을 때 사용하는 용도로 만들어진 데이터 타입이다. 그러므로 undefined보다는 null을 사용하는 것이 좋다.
이로써, undefined
는 값을 대입하지 않은 변수에 접근하려고 할 때 자바스크립트 엔진이 반환해주는 값으로 사용하고, null
은 비어있음을 명시적으로 나타내고자 할 때 할당하는 값으로 구분지어 사용할 수 있다.