객체
: 하나의 데이터만 담을 수 있는 '원시형' 데이터와는 달리 여러 자료형을 담을 수 있는 데이터
: { 중괄호 }를 이용하여 선언하며 중괄호 안에는 키:값 형태의 프로퍼티로 구성
: 키에는 문자형, 값에는 모든 자료형 사용 가능
: JSON 형식으로 작성
: 프로퍼티의 값은 수정 가능
const a = {
name: 'nemar',
age: 20,
friends: ['Jeniffer', 'Kaiser'],
};
console.log(a.friends);
대괄호 표기법
: 객체의 프로퍼티 쌍에 접근하기 위한 또 다른 방법
: a.friends 대신 a[friends]로 접근
: 해당하는 키가 없어도 오류가 발생되지 않음
: 키에 변수를 사용할 수 있음 (동적으로 사용가능)
: 문자열도 키로 사용 가능
let fruit = prompt("어떤 과일을 구매하시겠습니까?", "apple");
let bag = {
[fruit]: 5, // 변수 fruit에서 프로퍼티 이름을 동적으로 받아 옵니다.
};
in 연산자
: 객체안에 프로퍼티의 존재 여부 확인 (true / false)
for in 반복문
: 객체에 대한 반복문
: 객체의 값에 순차적으로 접근할 수 있음
const object = {
name: 'jamse',
age: 30,
};
for (const key in objec) {
console.log(key);
}
정수 프로퍼티
: 프로퍼티의 키가 정수면 자동 정렬되어 출력됨
프로퍼티 삭제
: delete obj.prop
메서드 단축구문
: 객체 리터럴 안에 메서드를 선언 할 때 단축하여 작성하는 방법
user = {
sayHi() { // "sayHi: function()"과 동일합니다.
alert("Hello");
}
};
자유로운 this 사용
: 자바스크립트에서는 모든 함수에 this 사용 가능
let user = { name: "John" };
let admin = { name: "Admin" };
function sayHi() {
alert( this.name );
}
// 별개의 객체에서 동일한 함수를 사용함
user.f = sayHi;
admin.f = sayHi;
// 'this'는 '점(.) 앞의' 객체를 참조하기 때문에
// this 값이 달라짐
user.f(); // John (this == user)
admin.f(); // Admin (this == admin)
admin['f'](); // Admin (점과 대괄호는 동일하게 동작함)
심볼형
: 유일한 식별자를 만들기 위한 자료형
: 유일성이 보장되기 때문에 값이 같아도 다른 데이터로 취급
: 암시적인 문자형 변환이 안되기 때문에 toString()을 통해 값 출력
let id1 = Symbol("id");
let id2 = Symbol("id");
alert(id1 == id2); // false
심볼형 키
: 키가 심볼인 경우 for in에서 제외되어 출력 (심볼형 프로퍼티 숨기기)
: 외부 스크립트나 라이브러리에서 참조 불가
let id = Symbol("id");
let user = {
name: "John",
[id]: 123 // "id": 123은 안됨
};
심볼형 키 접근하기 (ObjectAssign)
let id = Symbol("id");
let user = {
[id]: 123
};
let clone = Object.assign({}, user);
alert( clone[id] ); // 123
전역 심볼
// 전역 레지스트리에서 심볼을 읽습니다.
let id = Symbol.for("id"); // 심볼이 존재하지 않으면 새로운 심볼을 만듭니다.
// 동일한 이름을 이용해 심볼을 다시 읽습니다(좀 더 멀리 떨어진 코드에서도 가능합니다).
let idAgain = Symbol.for("id");
// 두 심볼은 같습니다.
alert( id === idAgain ); // true
전역 심볼 찾기
let globalSymbol = Symbol.for("name");
let localSymbol = Symbol("name");
alert( Symbol.keyFor(globalSymbol) ); // name, 전역 심볼
alert( Symbol.keyFor(localSymbol) ); // undefined, 전역 심볼이 아님
alert( localSymbol.description ); // name
참조에 의한 객체복사
let user = { name: 'John' };
let admin = user;
admin.name = 'Pete'; // 'admin' 참조 값에 의해 변경됨
alert(user.name); // 'Pete'가 출력됨. 'user' 참조 값을 이용해 변경사항을 확인함
객체병합 (assign)
: 목표객체에 동일한 이름의 키가 있는 경우 덮어쓰기 됨
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 }
생성자 함수
: 유사한 객체를 여러개 만들 때 유용한 함수
: 일반함수와 구분하기 위해 첫 글자를 대문자로 작성하는 것이 관례
function User(name) {
this.name = name;
this.sayHi = function() {
alert( "제 이름은 " + this.name + "입니다." );
};
}
let bora = new User("이보라");
bora.sayHi(); // 제 이름은 이보라입니다.
new target
: 새로운 객체를 만든다는 것을 명시하여 유연한 코드 작성
function User(name) {
if (!new.target) { // new 없이 호출해도
return new User(name); // new를 붙여줍니다.
}
this.name = name;
}
let bora = User("보라"); // 'new User'를 쓴 것처럼 바꿔줍니다.
alert(bora.name); // 보라
객체를 원시형으로 변환하기
- hint
: 세 종류로 구분되는 객체형의 변환의 구분 기준
: 목표로 하는 자료형
: string, number, default
- default
: 연산자가 기대하는 자료형이 확실치 않은 경우
// string
// 객체를 출력하려고 함
alert(obj);
// 객체를 프로퍼티 키로 사용하고 있음
anotherObj[obj] = 123;
// number
// 명시적 형 변환
let num = Number(obj);
// (이항 덧셈 연산을 제외한) 수학 연산
let n = +obj; // 단항 덧셈 연산
let delta = date1 - date2;
// 크고 작음 비교하기
let greater = user1 > user2;
// default
// 이항 덧셈 연산은 hint로 `default`를 사용합니다.
let total = obj1 + obj2;
// obj == number 연산은 hint로 `default`를 사용합니다.
if (user == 1) { ... };
Synbol.toPrimtive()
: 목표로 하는 자료형을 명명하는데 사용하는 함수
let user = {
name: "John",
money: 1000,
[Symbol.toPrimitive](hint) {
alert(`hint: ${hint}`);
return hint == "string" ? `{name: "${this.name}"}` : this.money;
}
};
// 데모:
alert(user); // hint: string -> {name: "John"}
alert(+user); // hint: number -> 1000
alert(user + 500); // hint: default -> 1500
toString / ValueOf
let user = {name: "John"};
alert(user.toString()); // [object Object]
alert(user.valueOf() === user); // true
let user = {
name: "John",
money: 1000,
// hint가 "string"인 경우
toString() {
return `{name: "${this.name}"}`;
},
// hint가 "number"나 "default"인 경우
valueOf() {
return this.money;
}
};
alert(user); // toString -> {name: "John"}
alert(+user); // valueOf -> 1000
alert(user + 500); // valueOf -> 1500
추가 형 변환
let obj = {
// 다른 메서드가 없으면 toString에서 모든 형 변환을 처리합니다.
toString() {
return "2";
}
};
alert(obj * 2); // 4, 객체가 문자열 "2"로 바뀌고, 곱셈 연산 과정에서 문자열 "2"는 숫자 2로 변경됩니다.
가비지 컬렉션
: 쓸모가 없게 된 값을 메모리에서 삭제하는 자바스크립트 엔진의 기능
: 자바스크립트에서는 도달 가능한 (reachability) 개념을 통해 메모리 관리 수행
: 도달 가능한 값은 메모리에서 삭제되지 않음
- 삭제되지 않는 값 (root)
: 현재 함수의 지역변수와 매개변수
: 중첩 함수의 체인이 있는 함수에서 사용되는 변수와 매개변수 (객체가 참조하는 변수)
: 전역변수
연결된 객체
: 값을 복사하면 delete를 통해 원본 객체의 키를 제거하면 도달할 수 없는 값으로 메모리에서 삭제됨
내부 알고리즘
- mark and sweep
: 가비지 컬렉터는 루트(root) 정보를 수집하고 이를 ‘mark(기억)’.
: 루트가 참조하고 있는 모든 객체를 방문하고 이것들을 ‘mark’.
: mark 된 모든 객체에 방문하고 그 객체들이 참조하는 객체도 mark 수행
: 한번 방문한 객체는 전부 mark 하기 때문에 같은 객체를 다시 방문하는 일은 없음
: 루트에서 도달 가능한 모든 객체를 방문할 때까지 위 과정을 반복
: mark 되지 않은 모든 객체를 메모리에서 삭제
자바스크립트 엔진에서 사용하는 가비지 컬렉션 최적화 기법
- generational collection(세대별 수집)
: 객체를 '새로운 객체’와 '오래된 객체’로 나눔
: 객체 상당수는 생성 이후 제 역할을 빠르게 수행해 금방 쓸모가 없어지는데, 이런 객체를 '새로운 객체’로 구분
가비지 컬렉터는 이런 객체를 공격적으로 메모리에서 제거
: 일정 시간 이상 동안 살아남은 객체는 '오래된 객체’로 분류하고, 가비지 컬렉터가 덜 감시
- incremental collection(점진적 수집)
: 방문해야 할 객체가 많다면 모든 객체를 한 번에 방문하고 mark 하는데 상당한 시간이 소모 됨
: 가비지 컬렉션에 많은 리소스가 사용되어 실행 속도가 느려짐
: 자바스크립트 엔진은 이런 현상을 개선하기 위해 가비지 컬렉션을 여러 부분으로 분리한 다음,
각 부분을 별도로 수행
: 작업을 분리하고, 변경 사항을 추적하는 데 추가 작업이 필요하긴 하지만,
긴 지연을 짧은 지연 여러 개로 분산시킬 수 있다는 장점 존재
- idle-time collection(유휴 시간 수집)
: 가비지 컬렉터는 실행에 주는 영향을 최소화하기 위해 CPU가 유휴 상태일 때에만 가비지 컬렉션을 실행
옵셔널 체이닝 ?.
: 객체의 키가 존재하지 않아도 오류를 출력하지 않도록 하는 문법 구조체 (연산자가 아님)
: 키가 null 이나 undefined일 경우 그대로 출력
: 읽기에는 사용할 수 있지만 쓰기에는 사용 불가
: delete와 연계하여 사용가능
let user = {}; // 주소 정보가 없는 사용자
alert( user?.address?.street ); // undefined, 에러가 발생하지 않습니다.
단락 평가
: ?.의 좌측 평가대상에 값이 없으면 평가를 멈춤
let user = null;
let x = 0;
user?.sayHi(x++); // 아무 일도 일어나지 않습니다.
alert(x); // 0, x는 증가하지 않습니다.
옵셔널 체이닝을 이용해 안전하게 프로퍼티를 읽는 방법
: 객체의 존재 여부가 확실치 않더라도 안전하게 프로퍼티를 읽을 수 있음
let user1 = {
firstName: "Violet"
};
let user2 = null; // user2는 권한이 없는 사용자라고 가정해봅시다.
let key = "firstName";
alert( user1?.[key] ); // Violet
alert( user2?.[key] ); // undefined
alert( user1?.[key]?.something?.not?.existing); // undefined
Refference
https://ko.javascript.info/object
https://ko.javascript.info/object-methods
https://ko.javascript.info/symbol
https://ko.javascript.info/object-copy
https://ko.javascript.info/constructor-new
https://ko.javascript.info/object-toprimitive
https://ko.javascript.info/garbage-collection
https://ko.javascript.info/optional-chaining
'Programming > JavaScript' 카테고리의 다른 글
모던 자바스크립트 (자료구조와 자료형) 3-2. 객체의 순환과 숫자형 (0) | 2022.03.31 |
---|---|
모던 자바스크립트 (자료구조와 자료형) 3-1. 원시 값 활용과 배열 관련 메서드 (0) | 2022.03.30 |
자바스크립트의 여러가지 연산자와 표현식 (0) | 2022.03.09 |
모던 자바스크립트 (자바스크립트 기본 / 코드 품질) 1. 코드 작성 및 테스트 (0) | 2022.02.26 |
JavaScript Promise와 aync/await의 차이점 <Promise / Promise.all / async await 자세히 분석하기> (0) | 2022.02.08 |