[Javascript] 프로토타입(prototype)

nxnaxx·2021년 11월 18일
0

javascript

목록 보기
12/14
post-thumbnail

객체지향 프로그래밍

 속성(property)을 통해 여러 개의 값을 하나의 단위로 구성한 자료구조를 객체(object)라 하며 객체의 집합으로 프로그램을 표현하는 것을 객체지향 프로그래밍이라 한다.

const circle = {
  radius: 2,
  circumference() {
    return 2 * 3.14 * this.radius;
  }
};

console.log(circle1.circumference()); // 12.56

위 예제에서 반지름 radius는 상태를 나타내는 데이터, 원의 둘레 circumference는 동작을 나타낸다. 이처럼 객체는 상태(state) 데이터와 동작(be-havior)을 하나의 논리적인 단위로 묶은 복합적인 자료구조로 상태 데이터를 프로퍼티(property), 동작을 메서드(method)라 부른다.


상속과 프로토타입

 상속(inheritance)은 어떤 객체의 프로퍼티나 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 말한다.

function Triangle(side) {
  this.side = side;

  this.getArea = function() {
    return Math.sqrt(3) / 4 * this.side ** 2; 
  };
}

const triangle1 = new Triangle(2);
const triangle2 = new Triangle(4);

console.log(triangle1.getArea()); // 1.7320508075688772
console.log(triangle2.getArea()); // 6.928203230275509

위 예제에서 Triangle 생성자 함수는 인스턴스를 생성할 때마다 getArea 메서드를 중복 생성하고 모든 인스턴스가 중복 소유한다. 동일한 메소드를 중복 소유하는 것은 메모리를 불필요하게 낭비하는 일로, 상속을 통해 불필요한 중복을 줄일 수 있다.

자바스크립트는 프로토타입(prototype)을 기반으로 상속을 구현한다.

📝 프로토타입(prototype)
프로토타입은 어떤 객체의 상위(부모) 역할을 하는 객체로 하위(자식) 객체에게 자신의 프로퍼티와 메서드를 상속한다. 상속받은 하위 객체는 자신의 프로퍼티나 메서드인 것처럼 자유롭게 사용할 수 있다.


상속으로 불필요한 중복을 제거하면 아래와 같다.
function Triangle(side) {
  this.side = side;
}

Triangle.prototype.getArea = function() {
  return Math.sqrt(3) / 4 * this.side ** 2;
};

const triangle1 = new Triangle(2);
const triangle2 = new Triangle(4);

console.log(triangle1.getArea()); // 1.7320508075688772
console.log(triangle2.getArea()); // 6.928203230275509

프로토타입 체인(prototype chain)

// prototype chain
function Hobby(sport) {
  this.sport = sport;
}

Hobby.prototype.intro = function() {
  console.log(`My hobby is ${this.sport}`);
};

const me = new Hobby('futsal');

console.log(me.hasOwnProperty('sport')); // true

 위 예제에서 Hobby 생성자 함수에 의해 생성된 me 객체는 Object.prototype의 메서드 hasOwnProperty를 호출할 수 있는데, 이는 me 객체가 Hobby.prototype뿐만 아니라 Object.prototype도 상속받았다는 것을 의미한다.

자바스크립트는 객체의 프로퍼티에 접근하려 할 때 해당 객체에 접근하려는 프로퍼티가 없다면 [[Prototype]] 내부 슬롯의 참조를 따라 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색하는데, 이를 프로토타입 체인(prototype chain)이라 한다.

체인 최상위에 위치하는 객체는 항상 Object.prototype이며 모든 객체는 이를 상속받는다. 그래서 Object.prototype프로토타입 체인의 종점(end of prototype chain)이라 부른다.


instanceof 연산자

 우변의 생성자 함수의 프로토타입에 바인딩된 객체가 좌변 객체의 프로토타입 체인 상에 존재하면 true, 그렇지 않으면 false로 평가된다.

// instanceof operator
function Hobby(sport) {
  this.sport = sport;
}

const me = new Hobby('futsal');
const parent = {};

Object.setPrototypeOf(me, parent);

console.log(me instanceof Hobby); // false
console.log(me instanceof Object); // true

me 객체의 프로토타입을 교체함으로써 Hobby.prototype이 me 객체의 프로토타입 체인 상에 존재하지 않게 되었다. 때문에 me instanceof Hobby는 false로 평가되는 것이다.

true로 평가되게 하려면 parent 객체를 Hobby 생성자 함수의 prototype 프로퍼티에 바인딩하면 된다.

// instanceof operator
function Hobby(sport) {
  this.sport = sport;
}

const me = new Hobby('futsal');
const parent = {};

Object.setPrototypeOf(me, parent);

Hobby.prototype = parent;

console.log(me instanceof Hobby); // true

프로퍼티 존재 확인

in 연산자

 in 연산자는 객체 내 특정 프로퍼티가 존재하는지 여부를 확인한다.

// in operator
const favorite = {
  color: 'blue',
  fruit: 'lemon'
};

console.log('color' in favorite); // true
console.log('animal' in favorite); // false

확인 대상 객체의 프로퍼티뿐만 아니라 상속받은 모든 프로토타입의 프로퍼티를 확인하므로 주의해야 한다.

console.log('toString' in favorite); // true

favorite 객체에 toString이라는 프로퍼티는 존재하지 않지만 객체가 속한 모든 프로토타입에서 toString 프로퍼티를 검색하기 때문에 결과값이 true로 나온다. (toString은 Object.prototype의 메서드이다.)

📌 in 연산자 대신 ES6에서 도입된 Reflect.has 메서드를 사용해도 된다.

// Reflect.has method
console.log(Reflect.has(favorite, 'color')); // true
console.log(Reflect.has(favorite, 'toString')); // true

Object.prototype.hasOwnProperty 메서드

 Object.prototype.hasOwnProperty 메서드를 사용해도 객체에 특정 프로퍼티가 존재하는지 여부를 확인할 수 있다. 객체 고유의 프로퍼티가 아닌 상속받은 프로토타입의 프로퍼티 키인 경우에는 false를 반환한다.

// Object.prototype.hasOwnProperty
console.log(favorite.hasOwnProperty('fruit')); // true
console.log(favorite.hasOwnProperty('toString')); // false

프로퍼티 열거

for...in 문

 for...in 문은 객체의 프로토타입 체인 상에 존재하는 모든 프로토타입의 프로퍼티 중 프로퍼티 어트리뷰트 [[Enumerable]] 값이 true인 프로퍼티를 순회하며 열거(enumeration)한다.

📝 프로퍼티 어트리뷰트 [[Enumerable]]
프로퍼티의 열거 가능 여부를 나타내며 불리언 값을 갖는다.

// for... in
const favorite = {
  color: 'blue',
  fruit: 'lemon'
};

for (const key in favorite) {
  console.log(`${key}: ${favorite[key]}`);
}

[실행결과]
color: blue
fruit: lemon

for...in 문은 상속받은 프로토타입의 프로퍼티까지 열거하므로 상속받은 프로퍼티를 제외하고 객체 자신의 프로퍼티만 열거하려면 Object.prototype.hasOwnProperty 메서드를 사용하면 된다.

// for... in
const favorite = {
  color: 'blue',
  fruit: 'lemon',
  __proto__: { animal: dog }
};

for (const key in favorite) {
  if (!favorite.hasOwnProperty(key)) continue;
  console.log(`${key}: ${favorite[key]}`);
}

[실행결과]
color: blue
fruit: lemon

Object.keys/values/entries 메서드

 객체 자신의 고유 프로퍼티만 열거하려면 for...in 문 대신 Object.keys/values/entries 메서드를 사용하는 것을 권장한다.

Object.keys는 객체 자신의 열거 가능한 프로퍼티 키를 배열로 반환한다.

// Object.keys
const favorite = {
  color: 'blue',
  fruit: 'lemon',
  __proto__: { animal: dog }
};

console.log(Object.keys(favorite)); // [ 'color', 'fruit' ]

Object.values는 객체 자신의 열거 가능한 프로퍼티 값을 배열로 반환한다.

console.log(Object.values(favorite)); // [ 'blue', 'lemon' ]

Object.entries는 객체 자신의 열거 가능한 프로퍼티 키와 값의 쌍의 배열을 배열에 담아 반환한다.

console.log(Object.entries(favorite)); // [ [ 'color', 'blue' ], [ 'fruit', 'lemon' ] ]

0개의 댓글