객체(object)
- 단 하나의 값. (유일함)
- 불변이었던 원시 값들과는 달리 여러 가지 값을 가질 수 있으며, 변할 수도 있음.
- 본질적으로 컨테이너.
- 내용물이 바뀌더라도 컨테이너가 바뀌는 건 아님.
- 하나의 객체 리터럴에 여러 종류의 값(프로퍼티; property 또는 멤버)을 넣을 수 있음.
- 어떤 타입이든 상관없고 다른 객체여도 괜찮음.
- 배열과 다르게 순서가 보장되지 않음.
- key(키)와 value(값)로 이루어짐.
- 마치 여러개의 변수의 식별자(identifier)와 값(value)이 뭉쳐있는 것과 유사.
- 키(이름)는 반드시 문자열 또는 심볼. (프로퍼티 식별자이기 때문에 식별자와 같음)
- 또한 공백이 불가능 하고
'
(따옴표)로 감싸주면 가능.
- 다만 유효한 식별자가 아님.
- 객체에는 데이터 프로퍼티와 접근자(accessor) 프로퍼티가 존재.
- 접근자 프로퍼티는
getter
함수와 setter
함수가 있고 나머지는 데이터 프로퍼티.
const hong1 = {
name: 'Hong',
age: 40,
};
const hong2 = { name: 'Hong', age: 40 };
객체 생성과 프로퍼티
- 원시값도
const
선언이 권장 사항이지만, 객체는 필연적.
- 원시값과 다르게
const
임에도 수정 할 수 있는 이유는 원시값은 불변 값 그 자체지만 객체는 컨테이너이기 때문에 확보한 일정 공간을 의미.
- 일정 공간의 주소가 변하는 것이 아닌, 내용물이 변하는 것을 의미.
- 객체는
{}
로 생성.
- JS에서
{}
는 코드 블럭, 객체, 비구조화 할당을 의미함.
- 선언과 동시에, 객체의 프로퍼티를 할당 가능.
const ironMan = {
name: '토니 스타크',
actor: '로버트 다우니 주니어',
alias: '아이언맨',
};
- 선언 먼저 하고, 필요할 때 프로퍼티를 추가 가능.
const obj = {};
obj.color = 'yellow';
const hong1 = { name: 'Hong', age: 40 };
hong1.face = 'suck';
console.log(hong1);
delete hong1.face;
console.log(hong1);
프로퍼티(멤버) 접근법
비구조화 할당(destructuring assignment)
객체 비구조화 할당
- 변수(상수) 이름(식별자)과 객체의 프로퍼티 이름(key)이 일치.
- 유효한 프로퍼티의 이름만 해당.
const ironMan = {
name: '토니 스타크',
actor: '로버트 다우니 주니어',
alias: '아이언맨',
};
const { actor } = ironMan;
console.log(actor);
const obj = { b: 2, c: 3, d: 4 };
const { a, b, c } = obj;
console.log(a);
console.log(b);
console.log(c);
console.log(d);
- 변수 선언과 비구조화 할당을 나누어 할때.
- JS는 표현식 좌변을 블록으로 해석하기 때문에 할당만 하는 경우에는 괄호가 필연적임.
- 즉,
{}
가 비구조화 할당을 의미한다는 것을 표현해야함.
- 선언과 할당이 동시에 일어나는 비구조화 할당은
const
또는 let
키워드가 함께 표함되어 있어 괄호가 필요하지 않음.
const obj = { b: 2, c: 3, d: 4 };
let a, b, c;
{ a, b, c } = obj;
const obj = { b: 2, c: 3, d: 4 };
let a, b, c;
({ a, b, c } = obj);
console.log(a);
console.log(b);
console.log(c);
const ironMan = {
name: '토니 스타크',
actor: '로버트 다우니 주니어',
alias: '아이언맨',
};
function print(hero) {
const { alias, name, actor } = hero;
const text = `${alias}(${name}) 역할을 맡은 배우는 ${actor} 입니다.`;
console.log(text);
}
print(ironMan);
const ironMan = {
name: '토니 스타크',
actor: '로버트 다우니 주니어',
alias: '아이언맨',
};
function print({ alias, name, actor }) {
const text = `${alias}(${name}) 역할을 맡은 배우는 ${actor} 입니다.`;
console.log(text);
}
print(ironMan);
key
를 그대로 변수이름으로 쓰지 않고, 새로운 이름에 할당.
let {key: newName} = object
const animal = {
name: '멍멍이',
type: '개',
};
const { name: nickname } = animal;
console.log(nickname);
객체 깊은 곳을 비구조화 할당
const deepObject = {
state: {
information: {
name: 'coldair',
languages: ['korean', 'english', 'chinese'],
},
},
value: 5,
};
const { name, languages } = deepObject.state.information;
const { value } = deepObject;
const extracted = { name, languages, value };
console.log(extracted);
const deepObject = {
state: {
information: {
name: 'coldair',
languages: ['korean', 'english', 'chinese'],
},
},
value: 5,
};
const {
state: {
information: { name, languages },
},
value,
} = deepObject;
const extracted = { name, languages, value };
console.log(extracted);
배열 비구조화 할당
- 변수(상수) 이름은 마음대로 이용가능. (순서가 보장)
- 배열 순서 대로 대응하게 됨.
- 대응 되어지지 않은 요소는 버려짐.
const arr = [1, 2, 3];
let [x, y] = arr;
console.log(x);
console.log(y);
console.log(typeof x);
console.log(typeof y);
const array = [1];
const [one, two = 2] = array;
console.log(one);
console.log(two);
let a = 5, b = 10;
let c;
c = a;
a = b;
b = c;
console.log(a);
console.log(b);
let a = 5, b = 10;
[a, b] = [b, a];
console.log(a);
console.log(b);
확산 연산자(spread operator)를 이용하면
- 배열 비구조화 할당 시 대응 되지 않은 요소를 한번에 묶어 새로운 배열에 할당.
...
키워드 이용.
const arr = [1, 2, 3, 4, 5];
let [x, y, ...rest] = arr;
console.log(x);
console.log(y);
console.log(rest);
console.log(typeof x);
console.log(typeof rest);
접근자(accessor) 프로퍼티
- 객체 프로퍼티에는 데이터 프로퍼티와 접근자(accessor) 프로퍼티 두 가지 존재.
- 접근자 프로퍼티는
setter
와 getter
두가지 함수로 구성.
- 접근자 프로퍼티는 함수 프로퍼티(매서드)와 유사하지만, 접근했을 때는 데이터 프로퍼티와 비슷하게 동작.
- 때문에 동적 프로퍼티라고 부르기도 함.
- 접근자 프로퍼티는 특정 값을 바꾸거나 조회할 때, 원하는 코드를 함께 실행.
- 일반 함수와 다르게 조회만 해도 호출. (
()
괄호가 쓰이지 않음; 일반 키 값처럼 사용)
getter함수
getter
함수에서는 반환 값(리턴값)이 필요.
const numbers = {
a: 1,
b: 2,
get sum() {
console.log('sum함수가 실행됩니다.!');
return this.a + this.b;
},
};
console.log(numbers.sum);
numbers.b = 5;
console.log(numbers.sum);
setter함수
const dog = {
_name: '멍멍이',
set name(value) {
console.log(`이름이 ${value}로 바뀝니다.`);
this._name = value;
},
};
console.log(dog._name);
dog.name = '뭉뭉이';
console.log(dog._name);
getter와 setter 함수를 이용한다면
const dog = {
_name: '멍멍이',
get name() {
console.log(`_name을 조회합니다.`);
return this._name;
},
set name(value) {
console.log(`이름이 ${value}로 바뀝니다.`);
this._name = value;
},
};
console.log(dog.name);
dog.name = '뭉뭉이';
console.log(dog.name);
const numbers = {
_a: 1,
_b: 2,
sum: 3,
calculate() {
console.log(`calculate`);
this.sum = this._a + this._b;
},
get a() {
return this._a;
},
get b() {
return this._b;
},
set a(value) {
this._a = value;
this.calculate();
},
set b(value) {
this._b = value;
this.calculate();
},
};
console.log(numbers.sum);
numbers.a = 5;
numbers.b = 7;
numbers.a = 9;
console.log(numbers.sum);
프로퍼티 나열
for...in
- 객체의 프로퍼티(key)를 하나씩 반환하는 루프.
- 기억해야 할 것은 순서가 보장되지 않는 나열.(객체는 순서가 보장되지 않음)
- 순서는 브라우저나 노드 등의 프로그램에서 속도나 효율 목적으로 언제든 바뀔 수 있음.
for...in
루프에는 키가 심볼 프로퍼티는 포함하지 않음.
const SYM = Symbol();
const o = { a: 1, b: 2, c: 3, [SYM]: 4 };
for (let prop in o) {
console.log(prop);
}
- 클래스의 설계 의도대로 사용하기 위해
hasOwnProperty
사용 권장.
const SYM = Symbol();
const o = { a: 1, b: 2, c: 3, [SYM]: 4 };
for (let prop in o) {
if (!o.hasOwnProperty(prop)) continue;
console.log(`${prop}: ${o[prop]}`);
}
- 배열에서 이용 가능하지만 권장 하지 않으며, 배열의 값(value)을 하나씩 반환하는 루프인
for...of
루프를 지향.
hasOwnProperty
를 사용하는 이유
- 객체 네이티브 메서드.
- 특정 프로퍼티의 소유 여부를 반환.
- 소유하지 않았거나 프로토타입 체인에 정의 되었다면
false
반환.
- 객체의 직접적인 속성인지 확인.
- 즉, 상속한 속성을 무시하고, 객체 자체에 정의된 속성만을 확인할 수 있게 해줌.
- 예기치 않은 에러를 방지하고 안전한 코드를 작성 가능하게 함.
const obj = {
a: {},
};
console.log(obj.hasOwnProperty('a'));
console.log(obj.hasOwnProperty('b'));
Object.keys
const SYM = Symbol();
const o = { a: 1, b: 2, c: 3, [SYM]: 4 };
console.log(Object.keys(o));