원시형(primitive type) : 오직 하나의 데이터(문자열, 숫자 등)만 담을 수 있음
객체형 : 다양한 데이터를 담을 수 있음
객체는 중괄호 안에 ‘키(key): 값(value)’ 쌍으로 구성된 프로퍼티(property) 를 여러 개 넣을 수 있는데, 키엔 문자형, 값엔 모든 자료형이 허용됩니다.
객체를 만들 때 프로퍼티 키가 대괄호로 둘러싸여 있는 경우, 이를 계산된 프로퍼티(computed property) 라고 부릅니다. 프로퍼티 이름을 변수에서 가져오겠다는 것을 의미합니다.
정수 프로퍼티(integer property)는 자동으로 정렬되고, 그 외의 프로퍼티는 객체에 추가한 순서 그대로 정렬됩니다.
객체가 할당된 변수를 복사하면 동일한 객체에 대한 참조 값이 하나 더 만들어짐
기존에 있던 객체와 똑같으면서 독립적인 객체를 만들고 싶다면?
let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
// permissions1과 permissions2의 프로퍼티를 user로 복사합니다.
Object.assign(user, permissions1, permissions2);
// now user = { name: "John", canView: true, canEdit: true }
그런데 프로퍼티가 다른 객체에 대한 참조 값일 경우?
깊은 복사(deep cloning) : user[key]의 각 값을 검사하면서, 그 값이 객체인 경우 객체의 구조도 복사해주는 반복문을 사용
자바스크립트는 도달 가능성(reachability) 이라는 개념을 사용해 메모리 관리를 수행합니다.
자바스크립트 엔진 내에선 가비지 컬렉터(garbage collector)가 끊임없이 동작합니다. 가비지 컬렉터는 모든 객체를 모니터링하고, 도달할 수 없는 객체는 삭제합니다.
가비지 컬렉션은 엔진이 자동으로 수행하므로 개발자는 이를 억지로 실행하거나 막을 수 없습니다.
객체는 도달 가능한 상태일 때 메모리에 남습니다.
참조된다고 해서 도달 가능한 것은 아닙니다. 서로 연결된 객체들도 도달 불가능할 수 있습니다.
객체 프로퍼티에 할당된 함수를 메서드(method) 라고 부릅니다.
메서드 내부에서 this 키워드를 사용하면 객체에 접근할 수 있습니다.
this 값은 런타임에 결정됩니다.
화살표 함수는 일반 함수와는 달리 ‘고유한’ this를 가지지 않습니다. 화살표 함수에서 this를 참조하면, 화살표 함수가 아닌 ‘평범한’ 외부 함수에서 this 값을 가져옵니다.
let user = {
firstName: "보라",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi(); // 보라
new User(...)를 써서 함수를 실행하면 아래와 같은 알고리즘이 동작합니다.
빈 객체를 만들어 this에 할당합니다.
함수 본문을 실행합니다. this에 새로운 프로퍼티를 추가해 this를 수정합니다.
this를 반환합니다.
생성자 함수엔 보통 return 문이 없습니다. 그런데 만약 return 문이 있다면 어떤 일이 벌어질까요?
function BigUser() {
this.name = "원숭이";
return { name: "고릴라" }; // <-- this가 아닌 새로운 객체를 반환함
}
alert( new BigUser().name ); // 고릴라
function SmallUser() {
this.name = "원숭이";
return; // <-- this를 반환함
}
alert( new SmallUser().name ); // 원숭이
?.은 ?.'앞’의 평가 대상이 undefined나 null이면 평가를 멈추고 undefined를 반환합니다. 이런 평가 방법을 단락 평가(short-circuit)라고 부릅니다.
심볼(symbol)은 유일한 식별자(unique identifier)를 만들고 싶을 때 사용합니다.
Symbol()을 사용하면 심볼값을 만들 수 있습니다.
설명이 같은 심볼 두 개를 만들고 이를 비교해보겠습니다. 동일 연산자(==)로 비교 시 false가 반환되는 것을 확인할 수 있습니다.
let id1 = Symbol("id");
let id2 = Symbol("id");
alert(id1 == id2); // false
심볼은 문자형이 아니기 때문에 심볼의 값을 확인하고 싶으면 다음과 같은 메서드나 프로퍼티를 사용해야 합니다.
let id = Symbol("id");
alert(id.toString()); // Symbol(id)가 얼럿 창에 출력됨
alert(id.description); // id
심볼을 이용하면 ‘숨김(hidden)’ 프로퍼티를 만들 수 있습니다. 숨김 프로퍼티는 외부 코드에서 접근이 불가능하고 값도 덮어쓸 수 없는 프로퍼티입니다. 심볼은 서드파티 코드에서 접근할 수 없기 때문에, 심볼을 사용하면 서드파티 코드가 모르게 객체에 식별자를 부여할 수 있습니다.
전역 심볼 레지스트리(global symbol registry) 안에 심볼을 만들고 해당 심볼에 접근하면, 이름이 같은 경우 항상 동일한 심볼을 반환해줍니다.
원시값을 기대하는 내장 함수나 연산자를 사용할 때 객체-원시형으로의 형 변환이 자동으로 일어납니다.
객체-원시형으로의 형 변환은 세 종류로 구분할 수 있습니다.
대개 "default"일 때와 "number"일 때를 동일하게 처리합니다.
객체-원시형 변환엔 다음 알고리즘이 적용됩니다.
obj.toString()만 사용해도 '모든 변환’을 다 다룰 수 있기 때문에, 실무에선 obj.toString()만 구현해도 충분한 경우가 많습니다. 반환 값도 ‘사람이 읽고 이해할 수 있는’ 형식이기 때문에 실용성 측면에서 다른 메서드에 뒤처지지 않습니다. obj.toString()은 로깅이나 디버깅 목적으로도 자주 사용됩니다.
여기서부터는 개념만 대충 이해했지, 실제 이 개념을 활용해서 코드를 짜 보라 하면 구글링으로 해결할 정도로 얄팍하게 정리했다. 앞으로 실습해보면서 익숙해지기