simbol은 ECMAScript 6에서 새로 나옴
원시 데이터 타입
더이상 쪼갤 수 없는 최소한의 데이터로 변경 불가능한 값(immutable value)
변경이 불가능하다는 뜻은 메모리 영역에서의 변경이 불가능하다는 뜻으로, 재할당은 가능하다
var str = 'Hello';
str = 'world';
//이떄는 Hello와 world는 각각 다른 방(메모리)에 들어가고, str은 world가 들어가있는 방(메모리)를 가르키도록 변경한 것
- 일반 변수의 value가 동일하다면 같은 해당 값(value)이 있는 방을 가르켜서 비교하면 true가 됨
- 객체의 경우 값(value)이 동일하더라도 각각 방으로 들어가기때문에 비교하면 false가 됨
객체는 참조형태로 전달하고 전달 받음
var user = {
name: 'Lee',
address: {
city: 'Seoul'
}
};
var myName = user.name; // 변수 myName은 string 타입이다.
user.name = 'Kim';
console.log(myName); // Lee
myName = user.name; // 재할당
console.log(myName); // Kim
- 객체의 가변성 : 대입연산자(=)를 사용 할 경우 원본훼손을 함
(만약 객체가 참조를 통해 공유되었다면 누군가가 원본을 변경하게되면 그 객체를 참조를 공유하는 모든 장소에서 영향을 받음)
객체의 변경이 필요한 경우에는 참조가 아닌 객체의 방어적 복사(defensive copy) 를 통해 새로운 객체를 생성해서 사용해야 한다.
=> but, 객체가 변경 가능한 데이터를 많이 가지고 있다면 오히려 부적절 할 수도있음
Immutabillity(변경불가성)
- 객체가 생성 된 이후 그 상태를 변경할 수 없는 디자인 패턴
- 함수형 프로그래밍의 핵심 원리
ES6에서 추가된 메소드
대입연산자(=)로 그냥 넣어주는게 아닌 해당 메소드를 사용하여 복사해주는 것
target객체로 소스객체의 프로퍼티를 복사함
target이 {}(빈객체)라면 소스객체만 복사해서 넣어 새 객체를 만듦
// Copy
const obj = { a: 1 };
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
console.log(obj == copy); // false
// Merge
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };
const merge1 = Object.assign(o1, o2, o3);
console.log(merge1); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, 타겟 객체(o1)가 변경된다!
// Merge
const o4 = { a: 1 };
const o5 = { b: 2 };
const o6 = { c: 3 };
const merge2 = Object.assign({}, o4, o5, o6);
console.log(merge2); // { a: 1, b: 2, c: 3 }
console.log(o4); // { a: 1 }, 타겟 객체({})에 소스만 넣어준다!
중첩된 객체의 복사
A객체 내에 B객체가 있을 경우 A를 assign으로 복제 해도 A객체는 각자 방을 잡지만 B객체는 동일한 방의 값을 가져다 씀
이 상태에서o2.score.push(3)
해주면 o1의 score도[1, 2, 3]
으로 변질된다
이렇게 o2를 복제 후o2.score.concat()
으로 복사를 해줘야 B객체(내부 객체)도 따로 방을 잡음
- Object.concat(추가할 값) : 배열 복사시 사용 / 추가 값이 없으면 그대로 복사
객체를 불변하게 만들 수 있음
한번 불변하게 만들면 풀 수 있는 방법은 없음. 원본을 복사해서 쓰는 방법뿐
var o1 = {name:'kim', score:[1,2]}
o1.name = 'lee';
console.log(o1); // {name:'lee', score:[1,2]}
/* freeze사용 : 바꿀 수 없음 */
var o1 = {name:'kim', score:[1,2]}
Object.freeze(o1);
o1.name = 'lee';
console.log(o1); /// {name:'kim', score:[1,2]}
/* freeze를 사용해도 내부 객체는 변경 가능 (중첩된 객체 변경 가능) */
var o1 = {name:'kim', score:[1,2]}
Object.freeze(o1);
o1.name = 'lee';
o1.score.push(3);
console.log(o1); /// {name:'kim', score:[1,2,3]}
내부 객체까지 불변하게 만들려면 Deep freeze를 해야함
function deepFreeze(obj) {
const props = Object.getOwnPropertyNames(obj); //user객체의 모든 속성들을 배열로 반환하여 props에 넣어줌
props.forEach((name) => {
const prop = obj[name]; //props의obj[name]들을 prop에 넣어줌
if(typeof prop === 'object' && prop !== null) {
//만약 prop이 object이며 prop이 null이 아니라면 해당 prop을 deepFreeze를 해준다
deepFreeze(prop);
}
});
return Object.freeze(obj); //user객체를 freeze시켜서 반환한다.
}
const user = {
name: 'Lee',
address: {
city: 'Seoul'
}
};
deepFreeze(user); //deepFreeze라는 이름의 함수로 user객체를 보내준다.
user.name = 'Kim'; // 무시된다
user.address.city = 'Busan'; // 무시된다
console.log(user); // { name: 'Lee', address: { city: 'Seoul' } }
Object.assign과 Object.freeze을 사용하여 불변 객체를 만드는 방법은 번거러울 뿐더러 성능상 이슈가 있어서 큰 객체에는 사용하지 않는 것이 좋다.
Facebook이 제공하는 방법
- npm을 사용하여 Immutable.js를 설치
$ npm install immutable
// Immutable.js의 Map 모듈을 임포트하여 사용
const { Map } = require('immutable')
const map1 = Map({ a: 1, b: 2, c: 3 })
const map2 = map1.set('b', 50)
map1.get('b') // 2 , map1.set(‘b’, 50)의 실행에도 불구하고 map1은 불변하였다.
map2.get('b') // 50 , map1.set()은 결과를 반영한 새로운 객체를 반환한다.
공부하며 정리&기록하는 ._. 씅로그