6장. 객체
6.1 객체 소개
쓰기 가능(writable) 속성: 프로퍼티에 값을 설정할 수 있는지 없는지
열거 가능(enumerable) 속성: for/in 루프에 프로퍼티 이름을 반환할지 안 할지
변경 가능(configurable) 속성: 프로퍼티를 삭제할 수 있는지 없는지, 속성을 바꿀 수 있는지 없는지
6.2 객체 생성
일반적인 빈 객체를 만드는 방법
let o = {}
let o = new Object()
let o = Object.create(Object.prototype);
6.3 프로퍼티 검색과 설정
object.property
object["property"]
둘 다 가능하지만, 프로퍼티가 어떤 값으로 들어올지 모르는 상황(함수의 인자 등)에서는 대괄호만 사용할 수 있다. (따옴표 없이)
let o = {}; // o는 Object.prototype에서 객체 메서드를 상속 받는다.
o.x = 1; // o의 자체 프로퍼티 x가 생겼다.
let p = Object.create(o); // p는 o와 Object.prototype에서 프로퍼티를 상속 받는다.
p.y = 2;// p의 자체 프로퍼티 y가 생겼다.
let q = Object.create(p); // q는 p, o, Object.prototype에서 프로퍼티를 상속 받는다.
q.z = 3;// q의 자체 프로퍼티 z가 생겼다.
let f = q.toString(); // toString은 Object.prototype에서 상속 받는다.
q.x + q.y // => 3; x와 y는 각각 o와 p에서 상속 받았다.
프로퍼티 할당은 프로토타입 체인을 검색해 할당이 허용되는지 확인한다.
예를 들어 객체 o 가 읽기 전용인 x 프로퍼티를 상속한다면 할당은 허용되지 않는다.
하지만 할당이 허용된다면 항상 원래(자식) 객체에 프로퍼티를 생성하거나 설정할 뿐, 프로토타입 체인에 존재하는 객체는 절대 수정하지 않는다. 프로퍼티를 검색할 때는 상속을 감안하지만, 설정할 때는 그렇지 않으므로 상속된 프로퍼티도 덮어 쓸 수 있다 (가릴 수 있다).


데이터 기술자(data descriptor)
이름 | 의미 |
enumerable (필수) | true 또는 false, 열거 가능한 지 여부. 반복문을 사용해서 나열할 수 있는가? |
configurable (필수) | true 또는 false, 제거 가능한 지 여부 |
value (선택) | 프로퍼티의 값, 기본은 undefined |
writable (선택) | true 또는 false, true면 읽기 쓰기 가능, false면 읽기만 가능 |
프로퍼티 할당은 실패하거나, 아니면 원래 객체에 프로퍼티를 생성 또는 설정한다는 규칙에는 한 가지 예외가 있다. o 가 x 프로퍼티를 상속하고 그 프로퍼티가 세터 메서드가 있는 접근자 프로퍼티라면 o 에 x 프로퍼티를 새로 만드는 대신 세터 메서드를 호출한다. 하지만 세터 메서드는 객체 o에 호출되는 것이지 해당 프로퍼티를 정의한 프로토타입 객체에 호출되는 것이 아니므로, 세터 메서드가 프로퍼티를 변경하더라도 o 에 변화가 있을 뿐 프로토타입 체인은 변하지 않는다.
// 이 객체는 점점 증가하는 시리얼 번호를 만든다.
const serialnum = {
// 이 데이터 프로퍼티에는 다음 시리얼 번호가 들어갑니다.
// 프로퍼티 이름에 있는 _는 이 프로퍼티를 내부에서만 사용한다는 힌트입니다.
_n: 0,
// 현재 값을 증가시켜 반환합니다.
get next() { return this._n++; },
// n에 새 값을 할당하지만 현재 값보다 커야 합니다.
set next(n) {
if (n > this._n) this._n = n;
else throw new Error("serial number can only be set to a larger value");
}
};
serialnum.next = 10; // 시리얼 번호 시작을 정합니다.
serialnum.next // => 10
serialnum.next // => 11: 실행할 때마다 값이 달라집니다.
serialnum.next // => 12

6.6 프로퍼티 열거
상속된 프로퍼티가 열거되는 것을 막고 싶을 때
let o = {x: 1, y: 2, z: 3}
for(let p in o) {
console.log(p);
}
let o1 = Object.create(o);
for(let p in o1) {
console.log(p); // x, y, z
}
let o1 = Object.create(o);
for(let p in o1) {
if(!(o1.hasOwnProperty(p))) continue; // 상속된 프로퍼티는 건너뛴다.
console.log(p); // undefined
}
6.7 객체 확장
Object.assign()
으로 객체를 복사할 때는 소스 객체를 인자 순서대로 처리하는데, 첫 번째 소스 객체의 프로퍼티는 대상 객체에 같은 이름의 프로퍼티를 덮어 쓰고, 두 번째 소스 객체가 있다면 그 객체의 프로퍼티가 첫 번째 소스 객체에 있는 같은 이름의 프로퍼티를 덮어 쓴다. 소스 객체에 게터 메서드가 있거나 대상 객체에 세터 메서드가 있다면 복사 도중에 이들이 호출되긴 하지만 메서드 자체를 복사하지는 않는다. 복사되는 것 같은데???
const serialnum = {
_n: 0,
get next() { return this._n++; },
set next(n) {
if (n > this._n) this._n = n;
else throw new Error("serial number can only be set to a larger value");
}
};
let serialnumCopy = Object.assign(serialnum);
복사된 것 아닌가요??
존재하지 않는 프로퍼티만 복사하도록 Object.assign()
을 변형해서 사용할 수 있다.
// Object.assignO과 마찬가지지만 기존 프로퍼티는 덮어 쓰지 않는다.
// 심벌 프로퍼티를 복사하지 않는 것은 똑같다.
function merge(target, ...sources) {
for(let source of sources) {
for(let key of Object.keys(source)) {
if (! (key in target)) { // Object.assign()과 다른 점입니다.
target[key] = source[key];
}
}
}
return target;
}
Object.assign({x: 1}, {x: 2, y: 2}, {y: 3, z: 4}) // => {x: 2, y: 3, z: 4}
merge({x: 1}, {x: 2, y: 2}, {y: 3, z: 4}) //=>{x: 1, y: 2, z: 4}
6.8 객체 직렬화
객체를 문자열로 변환하는 작업.
JSON은 '자바스크립트 객체 표기법(JavaScript Object Notation)의 약어이며 문법은 자바스크립트의 객체, 배열 리터럴과 아주 비슷하다. JSON 문법은 자바스크립트 문법의 부분 집합이며 자바스크립트 값 전체를 표현하지는 못한다. 객체, 배열, 문자열, 유한한 숫자, true, false, null은 모두 지원되고 직렬화와 복원이 가능하다. NaN, Infinity, -Infinity는 null로 직렬화된다. 함수, RegExp, Error 객체, undefined 값은 직렬화하거나 복원할 수 없다.
JSON.stringify()
문자열로 변환.
JSON.parse()
다시 객체로 돌림.
6.9 객체 메서드
valueOf
let point1 = {
x: 3,
y: 4
}
Number(point1) // => NaN
let point2 = {
x: 3,
y: 4,
valueOf: function() { return this.x + this.y; }
}
Number(point2) // => 7
toJSON
let point = {
x: 1,
y: 2
};
JSON.stringify([point]) // => '[{"x":1,"y":2}]'
let point = {
x: 1,
y: 2,
toString: function() {return `${this.x}, ${this.y}`;},
toJSON1: function() {return this.toString();}
};
JSON.stringify([point]) // => '["(1, 2)"]'
6.10 확장된 객체 리터럴 문법
단축 프로퍼티
// ES5
let x = 1, y = 2;
let o = {
x: x,
y: y
};
// ES6
let x = 1, y = 2;
let o = { x, y };
o.x + o.y // => 3
게터와 세터
프로그램이 접근자 프로퍼티의 값을 검색하면 자바스크립트는 인자 없이 게터 메서드를 호출한다.
이 메서드의 반환 값이 프로퍼티 접근 표현식의 값이다.
프로그램에서 접근자 프로퍼티의 값을 설정하려 하면 자바스크립트는 세터 메서드를 호출하고 할당 표현식의 오른쪽 값을 인자로 전달한다.
이 메서드가 프로퍼티 값 '세팅'을 담당한다. 세터 메서드의 반환 값은 무시한다.
프로퍼티에 게터와 세터 메서드가 모두 있으면 해당 프로퍼티는 읽기와 쓰기가 모두 가능한 프로퍼티이다. 게터 메서드 하나만 있다면 읽기 전용 프로퍼티이다. 세터 메서드 하나만 있다면 쓰기 전용 프로퍼티이며, 값을 읽으려 하면 항상 undefined로 평가된다.
접근자 프로퍼티는 한 개 혹은 두 개의 메서드 형태로 정의되며 그 이름은 프로퍼티 이름과 같다.
let p = {
// x와 y는 일반적인 데이터 프로퍼티
x: 1.0,
y: 1-0,
// r은 게터와 세터가 있는, 읽고 쓸 수 있는 접근자 프로퍼티
// 접근자 메서드 뒤에 콤마!
get r() { return Math.hypot(this.x, this.y); },
set r(newvalue) {
let oldvalue = Math.hypot(this.x, this.y);
let ratio = newwlue/oldvalue;
this.x *= ratio;
this.y *= ratio;
},
// theta는 게터만 있는 읽기 전용 접근자 프로퍼티
get theta() { return Math.atan2(this.y, this.x); }
};
p.r // => Math.SQRT2
p.theta // => Math.PI / 4
읽기 전용 접근자 프로퍼티, 읽기 전용 프로퍼티 차이 알기
'부트캠프 > 자바스크립트 완벽 가이드' 카테고리의 다른 글
8장. 함수 (0) | 2023.01.31 |
---|---|
7장 배열 (0) | 2023.01.26 |
5장. 문 (단순 내용 나열) (0) | 2023.01.07 |
4장 표현식과 연산자 - 스터디 정리 및 회고 (0) | 2023.01.03 |
4장. 표현식과 연산자 - 단축 평가, 비트 NOT(~), 2진수 음수 변환 (0) | 2022.12.29 |