프로토타입의 이해
(What) 프로토타입은 무엇인가?
- 자바스크립트의 모든 객체는 자신의 부모 역할을 하는 객체와 연결되어있고 이 부모 객체를 프로토타입이라고 한다.
- 객체지향 프로그래밍의 근간을 이루는 객체 간 상속을 구현하기 위해 사용된다. 프로토타입은 어떤 객체의 상위(부모) 객체의 역할을 하는 다른 객체에 공유 프로퍼티를 제공한다.
- 프로토타입을 상속받은 하위(자식) 객체는 상위 객체의 프로퍼티를 자신의 프로퍼티처럼 자유롭게 사용할 수 있다.
(Who) 누가 프로토타입을 사용하는가?
- 개발자: 객체 지향 프로그래밍(OOP) 개념을 자바스크립트에서 활용하는 개발자들이 프로토타입을 자주 사용한다.
(When) 언제 프로토타입을 사용하는가?
- 반복되는 기능이 필요할 때: 프로토타입을 사용하는 가장 흔한 상황은 여러 객체에 동일한 메서드나 속성이 필요할 때이다. 예를 들어, 게임에서 여러 캐릭터가 서로 다른 특성을 가지고 있지만, 모두 "이동"이라는 공통된 동작을 해야 할 때, 이 "이동" 동작을 프로토타입에 정의할 수 있다.
- 메모리 효율성을 고려할 때: 다수의 객체가 동일한 메서드를 가지게 될 때 메모리 사용을 최적화하고자 할 때도 프로토타입을 사용한다.
(Where) 어디서 프로토타입을 사용하는가?
- 객체를 생성하는 모든 곳에서: 프로토타입은 객체를 생성하는 모든 곳에서 사용된다. 특히 라이브러리나 프레임워크, 또는 복잡한 애플리케이션에서 자주 사용된다.
(Why) 왜 프로토타입을 사용하는가?
- 메모리 효율성
- 생성자 함수를 이용해 객체를 만들면, 그 함수 안에 있는 모든 메서드(기능)가 새롭게 만들어지는 객체마다 복사된다. 즉, 같은 기능을 가진 메서드가 여러 객체에 반복적으로 만들어지게 된다. 이것은 메모리를 비효율적으로 사용하게 된다.
- 프로토타입을 사용하면 같은 기능을 여러 객체에서 공유할 수 있기 때문에, 메모리를 절약할 수 있다. 하나의 메서드를 여러 객체가 공통으로 사용하게 되어 메모리 낭비가 줄어든다.
- 일관성
- 프로토타입에 정의된 메서드를 수정하면, 그 메서드를 사용하는 모든 객체에 즉시 반영된다. 이것은 코드의 일관성을 유지하는 데 도움이 된다.
- 유지보수의 용이성
- 프로토타입을 사용하면, 하나의 메서드만 수정하거나 확장하면 되기 때문에, 코드 유지보수가 훨씬 간편하다. 여러 곳에 똑같은 메서드를 수정할 필요가 없다.
(How) 어떻게 프로토타입을 사용하는가?
- 프로토타입에 메서드 추가하는 방법 : 프로토타입에 메서드를 추가하여 모든 인스턴스에서 공통적으로 사용할 수 있게 한다. 예를 들어, Array.prototype에 사용자 정의 메서드를 추가하면, 모든 배열에서 해당 메서드를 사용할 수 있다.
function Animal(name, sound) {
this.name = name;
this.sound = sound;
}
Animal.prototype.makeSound = function() {
console.log(this.name + " says " + this.sound);
};
var dog = new Animal("Dog", "Woof");
var cat = new Animal("Cat", "Meow");
dog.makeSound();
cat.makeSound();
- 프로토타입 취급 및 할당 : Object.getPrototypeOf와 Object.setPrototypeOf 메서드를 사용한다. __proto__로 객체가 상속받는 프로토타입 객체를 참조할 수 있지만, 모든 객체가 __proto__ 접근자 프로퍼티를 사용할 수 있는 것은 아니기 때문에 사용하지 않는 게 좋다.
const obj = Object.create(null);
console.log(obj.__protp__);
console.log(Object.getPrototypeOf(obj));
- 프로토타입 체인: 자바스크립트의 프로토타입 체인을 이용하여 상속을 구현한다. 객체가 특정 속성이나 메서드를 찾지 못하면, 그 객체의 프로토타입 체인을 따라가면서 최상위 부모 객체까지 속성이나 메서드를 검색한다.
프로토타입 퀴즈
아래 코드의 obj2의 프로토타입은 무엇인가요?
var obj = {
x: 10,
y: 20,
};
var obj2 = Object.create(obj);
정답
obj
Object.create(obj)객체를 생성하고 obj의 프로토타입으로 정의한다. 따라서 obj2의 프로토타입은 obj 다.
아래 코드는 console에 어떤 로그를 남길까요?
var obj = { x: 10, y: 20 };
var obj2 = { x: 20 };
var obj3 = Object.create(obj2);
console.log(obj3.y);
정답
undefined
obj3의 프로토타입은 obj2이기 때문에 y값이 정의되지 않아 undefined가 출력된다.
아래 코드의 출력 내용은 무엇인가요?
const obj = {
greet() {
console.log("Hello");
}
};
console.log(obj.greet.prototype);
정답
undefined
ES6 메서드 축약 표현으로 정의된 함수는 non-constructor이므로 prototype을 가지지 않는다.
아래 코드의 출력 내용은 무엇인가요?
const obj = {
a: 1,
};
Object.prototype.b = 2;
console.log("a" in obj);
console.log("b" in obj);
console.log(obj.hasOwnProperty("a"));
console.log(obj.hasOwnProperty("b"));
정답
true true true false
in 연산자는 확인 대상 객체의 프로퍼티 뿐만 아니라 확인 대상 객체가 상속받은 모든 프로토타입의 프로퍼티를 확인하기 때문에 주의해야 한다.
Object.prototype.hasOwnProperty 메서드는 객체의 고유의 프로퍼티 키인 경우에는 true를 반환하기 때문에 in 연산자보다 안전하다
아래 코드의 문제점은 무엇인가요?
function Item(name, price) {
this.name = name;
this.price = price;
}
Item.__proto__.displayInfo = function () {
console.log(this.name + " is for $ " + this.price);
};
var cake = new Item("Chocolate Cake", 10);
cake.displayInfo();
정답
__proto__은 객체가 상속받은 프로토타입 속성을 가리키기 때문에 Item에는 displayInfo이 정의되어 있지 않아 오류가 발생한다.
cake에 들어간 값은 { name: 'Chocolate Cake', price: 10 } 이다.
생성된 객체들이 공통으로 사용하는 메서드나 속성을 정의할 때는 __proto__이 아닌 prototype을 사용해야 한다.