자바스크립트 완벽 가이드(JavaScript: The Definitive Guide 7/E) - 6장 : 객체

SangHyun Park·2023년 11월 16일

JavaScript

목록 보기
9/9

6.2.3 프로토타입

  • JS 객체 거의 대부분은 자신과 연결된 두 번째 객체를 갖는다.
  • 두 번째 객체를 프로토타입이라 부르며, 첫 번째 객체는 프로토타입에서 프로퍼티를 상속
  • 객체 리터럴을 사용해 생성한 객체는 모두 같은 프로토타입 객체를 갖는다.
  • new 키워드와 생성자를 사용해 만든 객체를 생성자 함수의 prototype 프로퍼티 값을 자신의 프로토타입으로 사용
  • 예를 들어 Date.prototype은 Object.prototype에서 프로퍼티를 상속하므로 new Date()로 생성한 Date객체는 Date.prototype와 Object.prototype 양쪽에서 프로퍼티를 상속한다. 이렇게 이어지는 프로토타입 객체 사이의 연결을 프로토타입 체인이라고 부른다.

6.2.4 Object.create()

  • 임의의 프로토타입을 사용해 새 객체를 만들 때 사용
let o1 = Object.create({ x: 1, y: 2 });

console.log(o1.x + o1.y); // 3

let o2 = Object.create(null) // 아무것도 상속 x

let o3 = Object.create(Object.prototype) // Object prototype 을 상속 === {} , new Object()
  • 사용 목적 : 서드 파티 라이브러리에서(악의적인 의도가 없더라도) 객체를 변경하는 사고를 막는 것

6.3 프로퍼티 검색과 설정

6.3.2 상속

  • JS 객체에는 자체 프로퍼티도 있고, 프로토타입 객체에서 상속하는 프로퍼티도 있다.
  • 객체 o의 x프로퍼티를 가져온다고 가정. o에서 x 자체 프로퍼티가 없다면 o의 프로포타입 객체에서 x프로퍼티를 검색. 그럼에도 x는 없고 프로토타입이 있다면 또 프로토타입에서 검색.
  • x 프로퍼티를 찾거나 프로토타입이 null인 객체에 도달할 때까지 검색을 계속.
  • 객체의 prototype 속성은 자신이 어디에서 프로퍼티를 상속했는지 나타내는 체인을 형성
let unitcircle = { r: 1 };
let c = Object.create(unitcircle);
c.x = 1;
c.y = 1;
c.r = 2;
unitcircle.r; // 1 프로토타입은 영향 x

6.3.3 프로퍼티 접근 에러

  • 존재하지 않는 프로퍼티를 검색하는 것은 에러가 아니다. o의 자체 프로퍼티나 상속된 프로퍼티에서 x프로퍼티를 찾지 못한다면 접근 표현식 o.x 는 undefined로 평가.
  • 하지만 존재하지 않는 객체의 프로퍼티를 검색하려는 것은 에러이다.
// 장황하지만 명시적인 방법
let surname = undefined;
if (book) {
  if (book.author) {
    surname = book.author.surname;
  }
}

// surname, null, undefined 중 하나를 가져오는 간결하고 관용적인 방법
surname = book && book.author && book.author.surname;

// ?. 사용한 할당 표현식
let surname = book?.author?.surname;

6.4 프로퍼티 삭제

delect 연산자는 객체에서 프로퍼티를 삭제. 피연산자는 프로퍼티 접근 표현식이어야 한다.

  • delect는 자체 프로퍼티만 삭제할 뿐 상속된 프로퍼티는 삭제하지 않는다.
  • 상속된 프로퍼티를 삭제하려면 반드시 해당 프로퍼티를 정의한 프로토타입 객체에서 삭제.
  • delect 삭제에 성공했을때, 또는 존재하지 않는 프로퍼티 삭제를 시도하는 등 효과가 없었을 때도 true로 평가.
  • 변경가능 속성이 false인 프로퍼티는 제거 x
  • delect Object.prototype // false

6.5 프로퍼티 테스트

  • 객체는 프로퍼티 집합으로 볼 수 있으며 주어진 이름을 가진 프로퍼티 객체가 존재하는지 확인 할때 in 연산자, hasOwnProperty(), propertyIsEnumerable() 사용하거나 그냥 프로퍼티 검색하면 된다.
let o = { x: 1 };
"x" in o;
"y" in o;
"toString" in o; // true 상속된 프로퍼티지만 포함되어있으므로

o.hasOwnProperty("x");
o.hasOwnProperty("y");
o.hasOwnProperty("toString"); // false : toString은 상속된 프러파티

o.propertyIsEnumerable("x")
o.propertyIsEnumerable("toString") // 자체 프로퍼티 x
Object.prototype.propertyIsEnumerable("toString"); // flase 열거 불가
let o = { x: undefined };
o.x != undefined; // false 프로퍼티가 존재하지만 정의 x
o.y != undefined; // false 프로퍼티가 존재x
"x" in o; // true : 존재
"y" in o; // false 존재 x
delete o.x; // x 삭제
"x" in o; // false 이제 존재 x

6.6 프로퍼티 열거

  • 객체의 프로퍼티 저네를 순회해야 할 때도 있다.
  • 객체가 상속하는 내장 메서드는 열거 불가이지만, 우리가 추가한 프로퍼티는 기본적으로 열거 가능
let o = { x: 1, y: 2, z: 3 };
o.propertyIsEnumerable("toString"); // false 열거 불가능

for (let p in o) {
  console.log(p); // x, y, z,는 순회 하지만 toString은 x
}

for in 보다 객체의 프로퍼티 이름을 배열에 저장해서 for/of 루프를 사용하는 것이 더 쉬울 때가 많다.

  • Object.keys() : 열거 가능한 프로퍼티 이름을 배열로 반환.
  • Object.getOwnPropertyNames() : 이름이 문자열이기만 하면 열거 불가인 자체 프로퍼티 이름도 배열로반환
  • Object.getOwnPropertySymbols() : 열거 가능 여부 x , 심벌인 자체 프로퍼티를 반환
  • Reflect.ownKeys() : 열거 가능 여부 x , 문자열 심벌인지 따지지 않고 자체 프로퍼티 이름을 전부 반환

6.7 객체 확장

객체의 프로퍼티를 다른 객체에 복사하는 것은 흔한일

let target = { x: 1 },
  source = { y: 2, z: 3 };
  
for (let key of Object.keys(source)) {
  target[key] = source[key]
}

Object.assign()라는 유틸리티 함수 지원

  • o의 객체의 기본값이 존재하지 않는 다면 복사하려는 의도
o = Object.assign({}, defaults, o);

// 또는

o = { ...defaults, ...o };

6.8 객체 직렬화

  • 객체를 문자열로 변환 하는 작업
  • JSON.stringify()
  • JSON.parse()

6.9 객체 메서드

  • 명시적으로 프로토타입 없이 생성한 객체를 제외하면 객체는 모두 Object.prototype에서 프로퍼티를 상속
  • 이렇게 상속된 프로퍼티는 대부분 메서드이며, 어디서든 사용할 수 있다.

toString()

  • 호출한 객체의 값을 나타내는 문자열을 반환
  • 기본 toString() 메서드는 별로 도움되진 않는다.
let s = { x: 1, y: 1 }.toString() // s == '[obkect Object]'
let s = { x: 1, y: 1 }.toString(); // s == '[obkect Object]'

let point = {
  x: 1,
  y: 2,
  toString: function () {
    return `(${this.x}, ${this.y})`;
  },
};

String(point); // "(1, 2)" : 문자열로 변환할 때 toString()을 호출

toLocaleString()

  • 지역에 맞는 문자열 표현을 반환.
  • toString 예제와 비슷하게 쓰임

valueOf()

객체를 문자열이 아닌 다른 기본 타입, 보통은 숫자로 변환 하려할 때

Date 클래스의 valueOf()는 날짜를 숫자로 변환.

let point = {
  x: 1,
  y: 2,
  valueOf: function () {
    return Math.hypot(this.x, this.y);
  },
};
Number(point);

toJSON()

  • Object.prototype에 직접 정의 된 것은 아니지만 JSON.stringify() 메서드는 직렬화할 객체에서 JSON() 메서드를 검색
let point = {
  x: 1,
  y: 2,
  toJSON: function () {
    return this.toString();
  },
};

JSON.stringify([point]); // '["(1, 2)"]'

6.10 확장된 객체 리터럴 문법

6.10.1 단축 프로퍼티

번수 x와 y에 값을 저장했고 객체와 x와 y프로퍼티에도 그 값을 할당하고 싶다고할때

let x = 1, y = 2;
let o = { x, y }

간결하게 작성 가능

6.10.2 계산된 프로퍼티 이름

const PROPERTY_NAME = "p1";
function computePropertyName() {
  return "p" + 2;
}

let p = {
  [PROPERTY_NAME]: 1,
  [computePropertyName()]: 2,
};
  • 계싼된 프로퍼티 문법을 써서 라이브러리에서 정의하는 프로퍼티 이름 상수를 쓰는편이 더 안전하다.

6.10.3 프로퍼티 이름인 심벌

  • 심벌을 변수나 상수에 할당하면 계산된 프로퍼티 문법을 통해 그 심벌을 프로퍼티 이름으로 쓸 수 있다.
const extension = Symbol("my extension symbol");
let o = {
  [extension]: {
    /* 이 객체에 확장 데이터를 저장 */
  },
};
o[extension].x = 0;
  • 심벌은 불투명한 값이고 프로퍼티 이름 이외에 다른 용도로 쓸 수 없다.
  • 하지만 심벌은 다른 어떤 심벌과도 같지 않으므로 고유한 프로퍼티 이름을 만들 때 안성 맞춤
  • 심벌의 목적은 보안이 아니라 객체가 사용할 수 있는 안전한 확장 메커니즘을 만드는 것이다.
profile
마라토너

0개의 댓글