프로토타입(Prototype), 클래스, 인스턴스, 프로토타입의 관계
- -
프로토타입(Prototype)이란?
- 프로토타입은 원형 객체를 의미합니다.
- 한국어로는 원형정도로 번역되는 prototype은 말 그대로 객체의 원형이라고 할 수 있다.
함수는 객체다. 그러므로 생성자로 사용될 함수도 객체다. 객체는 프로퍼티를 가질 수 있는데 prototype이라는 프로퍼티는 그 용도가 약속되어 있는 특수한 프로퍼티다. prototype에 저장된 속성들은 생성자를 통해서 객체가 만들어질 때 그 객체에 연결된다. - ECMA-262에서 prototype은 object that provides shared properties for other objects로, 다른 객체에 공유 프로퍼티(메서드 포함)를 제공하는 객체이다.
- 정확히 말하자면 상속되는 속성과 메소드들은 각 객체가 아니라 객체의 생성자의 prototype이라는 속성에 정의되어 있다.
// ES5
function Ultra(){}
Ultra.prototype.ultraProp = true;
function Super(){}
Super.prototype = new Ultra();
function Sub(){}
Sub.prototype = new Super();
var o = new Sub();
console.log(o.ultraProp); // --> true
// ES6
class Ultra {
constructor() {}
}
Ultra.prototype.ultraProp = true;
class Super extends Ultra {
constructor() {
super();
}
}
class Sub extends Super {
constructor() {
super();
}
}
const o = new Sub();
console.log(o.ultraProp); // --> true
- 위 코드에서 Ultra 클래스는 프로토타입 객체를 통해 ultraProp 속성을 정의하였고,
- Super 클래스는 Ultra 클래스를 상속받아 ultraProp 속성을 상속받아 사용할 수 있다.
- 그리고 Sub 클래스는 Super 클래스를 상속받아 ultraProp 속성을 상속받아 사용할 수 있다.
- 그리고 o 객체는 Sub 클래스를 인스턴스화 하여 ultraProp 속성을 사용할 수 있다.
모든 객체는 [[Prototype]]이라는 내부 슬롯(자바스크립트 엔진의 내부 로직)을 갖으며, 상속을 구현하는 프로토타입 객체를 가르킨다.
하지만 [[Prototype]] 내부 슬롯에는 직접 접근이 불가하다. 이는 프로토타입 체인의 단방향을 지키기 위해서다. 만약 직접 접근가능하다면, 서로가 서로의 프로토타입이 되면서 프로토타입 체인이 무한으로 돈다. 따라서 __proto__ 프로퍼티로만 접근할 수 있다.
const a = {};
const b = {};
a.__proto__ = b;
b.__proto__ = a; // Uncaught TypeError: Cyclic __proto__ value
.prototype(함수의 prototype 프로퍼티)
- prototype 프로퍼티를 이용하면 현재 존재하고 있는 프로토타입에 새로운 프로퍼티나 메소드를 손쉽게 추가할 수 있다.
- 과거엔 프로토타입에 직접 접근할 수 있는 방법이 없었다. 그나마 믿고 사용할 수 있었던 방법은 이번 챕터에서 설명할 생성자 함수의 "prototype" 프로퍼티를 이용하는 방법뿐..
- 생성자 함수의 프로토타입이 객체인 경우에 new 연산자를 사용해 만든 객체는 생성자 함수의 프로토타입 정보를 사용해 [[Prototype]]을 설정한는 것이다.
- 생성자 함수에 기본으로 세팅되는 프로퍼티(F.prototype)는 [[Prototype]]과 다르다. F.prototype은 new F()를 호출할 때 만들어지는 새로운 객체의 [[Prototype]]을 설정합니다.
- F.prototype의 값은 객체나 null만 가능합니다. 다른 값은 무시된다.
- 생성자 함수를 new를 사용해 호출할 때만 적용됩니다.
프로토타입 체인을 이용한 상속
프로토타입 체인은 쉽게 생각해서 부모요소에 적용된 것들을 자식요소가 상속받아 자식요소에서도 사용할 수 있다고 생각하면 편할것 같다.
.__proto__ // 더 이상 사용되지 않음 자세히 보기
- 모던 브라우저들이 __proto__ (앞뒤로 언더바 2개씩) 속성을 통해 특정 객체의 프로토타입 객체에 접근할 수 있도록 구현하였다.
- 모든 객체는 __proto__를 통해 자신의 프로토타입([[Prototype]] 내부 슬롯)에 접근할 수 있다.
- ECMAScript 2015부터는 Object.getPrototypeOf(obj) 함수를 통해 객체의 프로토타입 객체에 바로 접근할 수 있게 되었습니다.
- ES6에서 __proto__를 표준으로 채택되었다. (사용은 지양한다)
- Object.prototype.__proto__는 오늘날 대부분의 브라우저에서 지원되지만 그 존재와 정확한 동작은 웹 브라우저와의 호환성을 보장하기 위해 레거시 기능으로 ECMAScript 2015 사양에서만 표준화되었다. 더 나은 지원을 위해 대신 Object.getPrototypeOf()를 사용을 권장한다.
- __proto__은 논란의 여지가 있으며 권장되지 않는다. 그 존재와 정확한 동작은 웹 호환성을 보장하기 위해 레거시 기능으로만 표준화되었지만 몇 가지 보안 문제가 있다. 더 나은 지원을 위해 Object.getPrototypeOf()/ Reflect.getPrototypeOf()및 Object.setPrototypeOf()/ Reflect.setPrototypeOf()를 대신 사용하길 권장한다.
const hello = { name: '김코딩' };
Object.getPrototypeOf(hello);
// {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
.__proto__는 객체의 프로토타입을 가리키는 프로퍼티입니다. 객체는 프로토타입 객체를 가리키는 링크를 가지고 있으며, 프로토타입 객체는 자신의 프로퍼티를 가지고 있다.
예를 들어, 아래와 같이 객체를 정의할 수 있다.
Copy code
let myObject = { a: 1, b: 2 };
이 객체는 Object.prototype 객체를 프로토타입으로 가르킨다. 따라서, myObject.__proto__는 Object.prototype을 가르킨다.
또한, 객체의 프로퍼티를 정의할 때 프로토타입 객체의 프로퍼티를 상속받을 수 있다. 예를 들어,
Copy code
let anotherObject = Object.create(myObject);
console.log(anotherObject.__proto__ === myObject); // true
console.log(anotherObject.a); // 1
anotherObject는 myObject를 프로토타입으로 가르킨다. 그리고 myObject의 프로퍼티 a를 상속받아 사용할 수 있다.
// div의 .__proto__ 값 확인시
let div = document.createElement('div');
//undefined
div.__proto__
//HTMLDivElement {Symbol(Symbol.toStringTag): 'HTMLDivElement', onmouseenter: undefined, onmouseleave: undefined, constructor: ƒ}
div.__proto__.__proto__
//HTMLElement {…}
div.__proto__.__proto__.__proto__
//Element {…}
div.__proto__.__proto__.__proto__.__proto__
//Node {…}
div.__proto__.__proto__.__proto__.__proto__.__proto__
//EventTarget {Symbol(Symbol.toStringTag): 'EventTarget', addEventListener: ƒ, dispatchEvent: ƒ, removeEventListener: ƒ, constructor: ƒ}
div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__
//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}constructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ toString()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__()__lookupSetter__: ƒ __lookupSetter__()__proto__: (...)get __proto__: ƒ __proto__()set __proto__: ƒ __proto__()
div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__
//null
클래스, 인스턴스, 프로토타입의 관계
- 클래스는 객체를 생성하는 틀, 프로토타입을 정의하는 공장 역할을 한다.
- 인스턴스는 클래스로부터 생성된 객체를 의미한다. 인스턴스는 클래스에서 정의된 프로퍼티와 메서드를 상속받아 사용할 수 있다.
- 프로토타입은 클래스나 인스턴스가 공유하는 기능을 정의하는 객체다. 인스턴스는 클래스로부터 상속받은 프로퍼티와 메서드를 프로토타입에서 상속받아 사용할 수 있다.
정리하자면, 클래스는 객체를 생성하는 틀을 정의하며 인스턴스는 클래스로부터 생성된 객체를 의미하며 프로토타입은 클래스나 인스턴스가 공유하는 기능을 정의하는 객체다.
class MyClass {
constructor() {
this.property = "I'm an instance property";
}
method() {
console.log("I'm an instance method");
}
}
MyClass.staticProperty = "I'm a static property";
MyClass.staticMethod = function () {
console.log("I'm a static method");
}
const instance = new MyClass();
console.log(instance.property); // "I'm an instance property"
instance.method(); // "I'm an instance method"
console.log(MyClass.staticProperty); // "I'm a static property"
MyClass.staticMethod(); // "I'm a static method"
위 코드에서 MyClass는 클래스로써 객체를 생성할 수 있도록 정의하였다.
property 와 method는 인스턴스의 프로퍼티와 메서드를 의미하며 staticProperty와 staticMethod는 클래스의 프로퍼티와 메서드를 의미한다. 그리고 instance는 MyClass 클래스로부터 생성된 인스턴스를 의미하며 MyClass 클래스로부터 상속받은 프로퍼티와 메서드를 사용할 수 있다.
JavaScript에서 언제 상속을 사용해야 할까?
프로토타입과 상속은 Javascript에서 가장 난해한 부분, 어찌보면 여러분은 항상 상속하고 있었습니다. Web API나 브라우저 내장 객체인 string, array 등의 메소드/속성을 사용하면서 암묵적으로 상속을 사용하고 있었던거죠.
참조:
https://developer.mozilla.org/ko/docs/Learn/JavaScript/Objects/Object_prototypes
https://poiemaweb.com/js-prototype
https://ko.javascript.info/function-prototype
http://www.tcpschool.com/javascript/js_object_prototype
https://www.howdy-mj.me/javascript/prototype-and-proto
'프론트엔드 공부 > 자바스크립트' 카테고리의 다른 글
Promise의 기능과 필요한 이유 (0) | 2023.02.10 |
---|---|
Beesbeesbees 과제 (0) | 2023.01.16 |
객체지향 프로그래밍 OOP (캡슐화, 상속, 추상화, 다형성) (0) | 2023.01.13 |
클래스(class)와 인스턴스(instance) 그리고 객체(object) (0) | 2023.01.13 |
고차함수, 일급객체, 내장고차함수(filter, map, reduce) (0) | 2023.01.12 |
소중한 공감 감사합니다