자바스크립트는 프로토타입 기반
객체지향 프로그래밍(OOP)를 지원하는 언어다. C++, Java와 같은 클래스 기반
객체지향 프로그래밍 언어의 특징인 클래스와, 상속, 캡슐화위한 public, private, protected 등이 없어 자바스크립트는 객체지향
언어가 아니라고 오해할 수 있지만 프로토타입 기반
의 객체지향 언어이다.
ES6 에서 클래스가 도입되어 새로운 클래스 기반 객체지향 모델이 제공된 것은 아니다. 자바스크립트의 클래스는 함수이며 기존 프로토타입 기반 패턴의
문법적 설탕(syntacitc suger)
이라고 할 수 있다.
클래스와 생성자 함수는 모두 프로토타입 기반의 인스턴스를 생성하지만 정확히 동일하게 동작하지 않는다. 클래스가 더욱 엄격하며 생성자 함수에서 제공하지 않는 기능들도 가지고 있다.
syntacitc suger
In computer science, syntactic sugar is syntax within a programming language that is designed to make things easier to read or to express.
사실 자바스크립트는 객체기반 언어이며 대부분이 객체로 구성되어있다. 원시타입을 제외한 참조타입, 정규표현식 등은 모두 객체로 이루어져있기 때문이다.
객체지향 프로그래밍은 프로그램을 명령어 또는 함수의 목록으로 보는 전통적인 명령형 프로그래밍의
절차지향적인 관점에서 벗어나 여러개의 독립적 단위, 객체
의 집합으로 프로그래밍하는 패러다임을 말한다.
객체지향 프로그래밍은 실세계의 실체를 인식하는 철학적 사고를 프로그래밍에 접목시키려는 경향이 있다. 그 실체는 특징이나 성질을 나타내는 속성을 가지고 있으며 이를 통해 실체를 인식하거나 구별할 수 있다.
다양한 속성중에 우리가 필요한 속성만 골라서 표현하는 것을 추상화라 한다.
const circle = {
// 반지름. 원의 상태를 나타내는 데이터. 나머지는 동작을 의미함.
radius: 5,
// 원의 지름: 2r
getDiameter() {
return 2 * this.radius
},
// 원의 둘레: 2*3.14*r
getPerimeter() {
return 2 * Math.PI * this.radius
},
// 원의 넓이: 3.14*r*r
getArea() {
return Math.PI * this.radius ** 2
}
}
console.log(circle) // 객체의 속성들
console.log(circle.getDiameter()) // 10
console.log(circle.getPerimeter()) // 31.41592653589793
console.log(circle.getArea()) // 78.53981633974483
이와같이 객체지향 프로그래밍은 객체의 상태(state)를 나타내는 데이터와 상태를 조작할 수 있는 동작을 하나의 단위로 묶어 생각한다. 객체는 상태 데이터와 동작을 하나의 논리적인 단위로 묶은 복합적인 자료구조
라고 할 수 있다.
여기서 객체의 상태 데이터를 프로퍼티, 동작을 메서드라 부른다.
또한 각 객체는 고유한 기능을 수행하면서 다른 객체들과의 관계성을 가질 수 있다. 서로 데이터를 주고받거나 처리할 수도 있다. 또는 프로퍼티, 메서드를 상속받아 사용할 수도 있다.
상속은 객체지향에서 중요한 개념으로 객체의 프로퍼티, 메서드를 다른 객체가 상속받아 사용할 수 있는 것을 말한다.
자바스크립트는 프로토타입을 기반으로 상속을 구현하여 불필요한 중복을 제거한다. 이를 실현하기 위해서는 코드의 재사용을 활용하는 것이다.
// 생성자 함수
function Circle(radius) {
this.radius = radius
this.getArea = function() {
// 원주율
return Math.PI * this.radius ** 2
}
}
// 반지름이 1인 인스턴스 생성
const circle1 = new Circle(1)
// 반지름이 2인 인스턴스 생성
const circle2 = new Circle(2)
// Circle 생성자 함수는 인스턴스를 생성할 때마다 동일한 동작을 하는
// getArea 메서드를 중복 생성하고 모든 인스턴스가 중복 소유한다.
// getArea 메서드는 하나만 생성하여 모든 인스턴스가 공유할 수 있도록 하는 것이 좋다.
console.log(circle1.getArea === circle2.getArea) // false
console.log(circle1.getArea()) // 3.141592653589793
console.log(circle2.getArea()) // 12.566370614359172
위의 코드의 문제점은 Circle 생성자 함수가 생성하는 모든 객체(인스턴스)는 radius 프로퍼티와 getArea 메서드를 갖는다. radius 프로퍼티 값은 일반적으로 인스턴스마다 다르다. 하지만 getArea 메서드는 모든 인스턴스가 동일한 내용의 메서드를 사용하므로 따로 분리하여 인스턴스들이 공유할 수 있도록 하는 것이 바람직하다는 것이다.
Circle 생성자 함수는 인스턴스를 생성할 때마다 getArea 메서드를 중복 생성하고 모든 인스턴스가 중복된 메서드를 소유하고 있기 때문에 메모리, 성능적으로 비효율적이다.
// 생성자 함수
function Circle(radius) {
this.radius = radius
}
// Circle 생성자 함수가 생성한 모든 인스턴스가 getArea 메서드를 공유해서
// 사용할 수 있도록 프로토타입에 추가한다.
// 프로토타입은 Circle 생성자 함수의 prototype 프로퍼티에 바인딩되어 있다.
Circle.prototype.getArea = function() {
return Math.PI * this.radius ** 2
}
// 인스턴스 생성
const circle1 = new Circle(1)
const circle2 = new Circle(2)
// Circle 생성자 함수가 생성한 모든 인스턴스는 부모 객체의 역할을 하는
// 프로토타입 Circle.prototype 으로부터 getArea 메서드를 상속받는다.
// 즉, Circle 생성자 함수가 생성하는 모든 인스턴스는 하나의 getArea 메서드를 공유하게 되는 것이다.
console.log(circle1.getArea === circle2.getArea) // true
console.log(circle1.getArea()) // 3.141592653589793
console.log(circle2.getArea()) // 12.566370614359172
자바스크립트는 프로토타입
기반으로 상속을 구현하기 때문에 위와 같이 활용하여 불필요한 중복을 제거한다.
Circle 생성자 함수가 생성한 모든 인스턴스는 자신의 프로토타입, 즉 부모 객체 역할인 Circle.prototype의 모든 프로퍼티와 메서드를 상속
받는다.
getArea 메서드는 단 하나만 생성되어 프로토타입인 Circle.prototype의 메서드로 할당되어 있다. 이로써 Circle 생성자 함수가 생성하는 모든 인스턴스들은 getArea 메서드를 상속받아 사용할 수 있게 되는 것이다.
이와같이 상속은 코드의 재사용성의 관점에서 매우 효율적이다. 공통적으로 사용할 프로퍼티나 메서드를 프로토타입에 미리 구현해 두면 생성자 함수가 생성할 인스턴스는 별도의 구현없이 프로토타입의 속성을 사용할 수 있기 때문이다.