자바스크립트는 프로토타입 기반의 객체지향 프로그래밍 언어이며 자바스크립트를 이루고 있는 거의 '모든 것'이 객체다.
1. 객체지향 프로그래밍
객체지향 프로그래밍 : 절차지향적 관점에서 벗어나 여러 개의 독립적 단위인 객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임
- 객체지향 프로그래밍은 실세계의 실체를 인식하는 철학적 사고를 프로그래밍에 접목하려는 시도에서 시작
- 실체는 특징이나 성질을 나타내는 속성을 가지고 있음
- 객체 : 상태 데이터와 동작을 하나의 논리적인 단위로 묶은 복합적인 자료구조
- 추상화 : 다양한 속성 중에서 필요한 속성만 간추려 표현하는 것
2. 상속과 프로토타입
상속 : 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것
자바스크립트는 프로토타입을 기반으로 상속을 구현하여 불필요한 중복을 제거
// 생성자 함수
function Circle(radius) {
this.radius = radius;
}
// Circle 생성자 함수가 생성한 모든 인스턴스가 getDiameter 메서드를
// 공유해서 사용할 수 있도록 프로토타입에 추가한다.
// 프로토타입은 Circle 생성자 함수의 prototype 프로퍼티에 바인딩되어 있다.
Circle.prototype.getDiameter = function () {
return Math.PI * this.radius ** 2;
};
// 인스턴스 생성
const circle1 = new Circle(1);
const circle2 = new Circle(2);
// Circle 생성자 함수가 생성한 모든 인스턴스는 부모 객체의 역할을 하는
// 프로토타입 Circle.prototype으로부터 getDiameter 메서드를 상속받는다.
// 즉, Circle 생성자 함수가 생성하는 모든 인스턴스는 하나의 getDiameter 메서드를 공유한다.
console.log(circle1.getDiameter === circle2.getDiameter); // true
console.log(circle1.getDiameter()); // 3.141592653589793
console.log(circle2.getDiameter()); // 12.566370614359172
위 예제와 같이 Circle 생성자 함수가 생성한 모든 인스턴스는 자신의 프로토타입인 Circle.prototype의 모든 프로퍼티와 메서드를 상속받는다. 그렇게 때문에 Circle 생성자 함수가 생성하는 모든 인스턴스는 getDiameter함수를 상속받아 사용할 수 있다. 이는 코드 재사용 면에서 매우 유리하다.
3. 프로토타입 객체
- 프로토타입 객체 : 객체지향 프로그래밍의 근간을 이루는 객체 간 상속을 구현하기 위해 사용
- 프로토타입 : 어떤 객체의 상위 객체의 역할을 하는 객체
- 다른 객체에 공유 프로퍼티를 제공
- 프로토타입을 상속받은 하위 객체는 상위 객체의 프로퍼티를 자신의 프로퍼티처럼 자유롭게 사용할 수 있다.
- 모든 객체는 [[Prototype]]이라는 내부 슬롯을 가지며, 이 내부 슬롯의 값은 프로토타입의 참조이다.
- [[Prototype]]에 저장되는 프로토타입은 객체 생성 방식에 의해 결정된다.
=> 객체가 생성될 때 객체 생성 방식에 따라 프로토타입이 결정되고 [[Prototype]]에 저장 - 모든 객체는 하나의 프로토타입을 갖는다.
- 모든 프로토타입은 생성자 함수와 연결되어 있다.
1. __proto__ 접근자 프로퍼티
모든 객체는 __proto__ 접근자 프로퍼티를 통해 자신의 프로토타입인 [[Prototype]] 내부 슬롯에 간접적으로 접근할 수 있다.
이렇게 모든 객체는 __proto__ 접근자 프로퍼티를 통해 프로토타입을 가리키는 [[Prototype]] 내부 슬롯에 접근할 수 있다.
- __proto__는 접근자 프로퍼티다
- 내부 슬롯은 프로퍼티가 아니므로 원칙적으로 내부 슬롯과 내부 메서드에 직접적으로 접근하거나 호출할 수 있는 방법을 제공하지 않는다.
- 일부 내부 슬롯과 내부 메서드에 한해 간접적으로 접근할 수 있다.
- __proto__ 접근자 프로퍼티는 상속을 통해 사용된다.
- 객체가 직접 소유하는 프로퍼티가 아니라 Object.prototype의 프로퍼티이다.
const person = { name: 'Lee' }; // person 객체는 __proto__ 프로퍼티를 소유하지 않는다. console.log(person.hasOwnProperty('__proto__')); // false // __proto__ 프로퍼티는 모든 객체의 프로토타입 객체인 Object.prototype의 접근자 프로퍼티다. console.log(Object.getOwnPropertyDescriptor(Object.prototype, '__proto__')); // {get: ƒ, set: ƒ, enumerable: false, configurable: true} // 모든 객체는 Object.prototype의 접근자 프로퍼티 __proto__를 상속받아 사용할 수 있다. console.log({}.__proto__ === Object.prototype); // true
- 객체가 직접 소유하는 프로퍼티가 아니라 Object.prototype의 프로퍼티이다.
- __proto__ 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유
- => 상호 참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위해서
- __proto__ 접근자 프로퍼티를 코드 내에서 직접 사용하는 것은 권장하지 않는다.
- 모든 객체가 __proto__ 접근자 프로퍼티를 사용할 수 있는 것은 아니기 때문에
// obj는 프로토타입 체인의 종점이다. 따라서 Object.__proto__를 상속받을 수 없다. const obj = Object.create(null); // obj는 Object.__proto__를 상속받을 수 없다. console.log(obj.__proto__); // undefined // 따라서 Object.getPrototypeOf 메서드를 사용하는 편이 좋다. console.log(Object.getPrototypeOf(obj)); // null
- 프로토타입의 참조를 취득하고 싶은 경우에는 Object.PrototypeOf 메서드를 사용
- 프로토타입을 교체하고 싶은 경우에는 Object.setPrototypeOf 메서드를 사용
const obj = {}; const parent = { x: 1 }; // obj 객체의 프로토타입을 취득 Object.getPrototypeOf(obj); // obj.__proto__; // obj 객체의 프로토타입을 교체 Object.setPrototypeOf(obj, parent); // obj.__proto__ = parent; console.log(obj.x); // 1
- 모든 객체가 __proto__ 접근자 프로퍼티를 사용할 수 있는 것은 아니기 때문에
2. 함수 객체의 prototype 프로퍼티
함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킨다.
// 함수 객체는 prototype 프로퍼티를 소유한다.
(function () {}).hasOwnProperty('prototype'); // -> true
// 일반 객체는 prototype 프로퍼티를 소유하지 않는다.
({}).hasOwnProperty('prototype'); // -> false
- non-constructor인 화살표 함수와 ES6 메서드 축약 표현으로 정의한 메서드는 prototype 프로퍼티를 소유하지 않고 프로토타입도 생성하지 않는다.
- 모든 객체가 가지고 있는 __proto__ 접근자 프로퍼티와 함수 객체만 가지고 있는 prototype 프로퍼티는 동일한 프로토타입을 가리킨다.
// 생성자 함수 function Person(name) { this.name = name; } const me = new Person('Lee'); // 결국 Person.prototype과 me.__proto__는 결국 동일한 프로토타입을 가리킨다. console.log(Person.prototype === me.__proto__); // true
3. 프로토타입의 constructor 프로퍼티와 생성자 함수
- 모든 프로토타입은 constructor 프로퍼티를 갖는다.
- constructor 프로퍼티는 prototype 프로퍼티로 자신을 참조하고 있는 생성자 함수를 가리킨다.
위 예시에서 Person 생성자 함수는 me 객체를 생성했다. 이 때, me 객체는 프로토타입의 constructor 프로퍼티를 통해 생성자 함수와 연결된다. 결국, me 객체에는 constructor 프로퍼티가 없지만 그의 프로토타입인 Person.prototype에는 constructor 프로퍼티가 있으므로 상속받아 사용할 수 있다.
'자바스크립트' 카테고리의 다른 글
[모던자바스크립트 Deep Dive] 19. 프로토타입(3) (0) | 2022.01.14 |
---|---|
[모던자바스크립트 Deep Dive] 19. 프로토타입(2) (0) | 2022.01.10 |
[모던자바스크립트 Deep Dive] 18. 함수와 일급 객체 (0) | 2021.12.22 |
[모던자바스크립트 Deep Dive] 17. 생성자 함수에 의한 객체 생성 (0) | 2021.12.15 |
[자바스크립트] Null값 체크, 유효성 검사 (0) | 2021.12.08 |