Ch6 에서는 카피 온 라이트를 만족하는 코드를 어떻게 짤 수 있는지에 대한 내용을 소개하고 있다.
Ch6 요약
카피 온 라이트는 복사본 만들기 - 복사본 변경하기 - 복사본 반환하기의 과정이 필요하다. 중첩 구조일 경우 각각 카피 온 라이트를 수행한다.
카피 온 라이트는 값을 변경하기 전 얕은 복사를 하고 리턴한다. ( 통제 가능한 범위에서 불변성 구현 가능 )
보일러 플레이트 코드를 줄이기 위해 기본적인 배열, 객체 동작에 대한 카피 온 라이트 버전을 만들어 두는게 좋다.
- 예를 들어 배열의 경우 slice, 객체의 경우 Object.assign 을 사용할 수 있다.
카피 온 라이트는 쓰기를 읽기로 전환시켜준다.
만약 쓰기와 읽기가 동시에 일어난다면 쓰기를 카피 온 라이트를 통해 2개의 읽기로 바꾸거나 한번에 2개의 값을 읽기를 통해 반환할 수 있다.
이 챕터에서는 배열, 객체에서 유용한 메서드, 프로퍼티를 통해 카피 온 라이트에 적용하는 방법을 알려준다.
불변 데이터를 읽는 것은 계산이고 변경 가능한 데이터를 읽는 것은 액션이다.
자바스크립트 배열 훑어보기
array[idx] - 인덱스로 값 찾기
array[]= - 값 할당하기
array.length - 프로퍼티이므로 괄호를 쓰지 않는다.
array.push(el) - 배열 끝에 el 을 추가하고 새로운 길이를 리턴
array.pop() - 배열 끝에 있는 값을 지우고 지운 값을 리턴
array.unshift(el) - 배열 맨 앞에 el 을 추가하고 새로운 길이 리턴
array.shift() - 배열 맨 앞에 있는 값을 지우고 지운 값을 리턴
array.slice() - 배열을 얕게 복사하여 새로운 배열 리턴
array.slice(idx, num) - idx 위치에서 num 개의 항목을 지우고 지운 항목을 배열로 리턴한다.
자바스크립트 객체 훑어보기
obj[key] - 키로 값 찾기
obj.key - 키로 값 찾기
obj.key = / obj[key] = - 키로 값 설정
delete obj.key / delete obj[key] - 키 / 값 쌍 지우기
Object.assign({}, obj) - 객체 복사하기
Object.keys(obj) - 키 목록 가져오기
배열에서 인덱스로 찾는것은 시간복잡도로 볼 때 O(n) 이기에 더 나은 성능을 위해 O(log n) 구조로 조회하는 방법을 사용할 수도 있다.
function add_element_last(array, elem) {
var new_array = array.slice(); // 👉 1. 복사본 만들기
new_array.push(elem); // 👉 2. 복사본 바꾸기
return new_array; // 👉 3. 복사본 반환하기
}
// 원본
var mailing_list = [];
function add_contact(email) {
mailing_list.push(email);
}
function submit_form_handler(event) {
var form = event.target;
var email = form.elements["email"].value;
add_contact(email);
}
/// Copy-on-write
var mailing_list = [];
function add_contact(mailing_list, email) {
var list_copy = mailing_list.slice();
list_copy.push(email);
return list_copy;
}
function submit_form_handler(event) {
var form = event.target;
var email = form.elements["email"].value;
mailing_list = add_contact(mailing_list, email);
}
shift()
메서드는 맨 앞의 요소를 제거하고 해당 요소를 반환한다. 이 메서드를 읽기와 쓰기 동작으로 분리해보자.
function first_element(array) { // 읽기
return array[0];
}
function drop_first(array) { // 쓰기
array.shift();
}
// Copy-on-write
function drop_first(array) {
var array_copy = array.slice();
array_copy.shift();
return array_copy;
}
function shift(array) {
return array.shift();
}
/// Copy-on-write
function shift(array) {
var array_copy = array.slice();
var first = array_copy.shift();
return {
first : first,
array : array_copy
};
}
function shift(array) {
return {
first : first_element(array),
array : drop_first(array)
};
}
변경 가능한 데이터를 읽는 것은 액션이다.
쓰기는 데이터를 변경 가능한 구조로 만든다.
어떤 데이터에 쓰기가 없다면 데이터는 변경 불가능한 데이터이다.
불변 데이터 구조를 읽는 것은 계산이다.
쓰기를 읽기로 바꾸면 코드에 계산이 많아진다.
언제든 최적화할 수 있다.
가비지 콜렉터는 매우 빠르다.
생각보다 많이 복사하지 않는다.
함수형 프로그래밍 언어에는 빠른 구현체가 있다.
얕은 복사 - 중첩된 데이터 구조에 최상위 데이터만 복사
- 객체가 들어 있는 배열의 경우 얕은 복사는 배열만 복사하고 안에 있는 객체는 참조로 공유한다.
구조적 공유 - 두 중첩된 데이터 구조에서 안쪽 데이터가 같은 데이터를 참조한다.
- 메모리를 적게 사용하고 모든 것을 복사하는 것보다 빠르다.
의문점인 부분
이미지 출처 : https://www.yes24.com/Product/Goods/108748841
이게 slice 를 나타내지 않는 것을수도 있는데 코드를 실행해봤을때 얕은 복사면 같은 참조를 바라봐야 하는데 copy 가 arr 와 다르다.
배열은 복사되어 다른 참조를 갖는데 안에 있는 안쪽 데이터는 참조만 복사하여 같은 데이터를 참조하고 있다. 책의 처 예시에서 무엇을 가리키는 것인지 그래서 의문이 들었다..
의문 해소
얕은 복사는 최상위 속성만 복사하므로 최상위 속성의 값을 변경해도 원본에 영향이 없으며 중첩 객체 속성을 재할당하면 원본 객체에 영향을 끼친다. 객체 참조 경우 복사본을 변경시 원본에 영향을 끼치나 객체 참조가 아닐 경우에는 원본에 영향을 끼치지 않는다. slice() 는 얕은 복사를 한다. 그러나 위의 예시에서는 객체 참조의 중첩 객체에 값을 재할당했으니 원본도 같이 바뀐 것이다.